2016-09-14 61 views
4

在像Haskell这样的面向功能的语言中,可以将函数定义重载为参数签名的多个轴。 C++支持数量和类型的参数。其他语言支持的参数值,甚至后卫条款(代码测试参数的条件。)例如在Haskell阶乘实现:有没有办法根据参数值进行函数签名匹配?

factorial :: (Integral a) => a -> a 
factorial 0 = 1 
factorial n = n * factorial (n - 1) 

凡阶乘的定义时参数是自定义的阶乘0不同当参数是任何其他整数。

我还没有在C++中找到这种功能,并且首先想到在语言中实现起来会很困难。进一步的思考让我觉得它实际上会相当简单,并且是语言的一个很好的补充,所以我必须忽略它。

有没有什么办法可以在本地语法或模板中做到这一点?

+0

C++模板可以专门用于整型常量,它与Haskell例子大致相同。 [这是用C++中的专用模板编写的因子函数](http://stackoverflow.com/q/3082113/464709)。 –

+0

是的,只有在编译时已知参数值时才可以使用模板元编程。除了通过虚拟方法调度以外,C++不能在运行时分派参数值。 – antlersoft

+0

这不是两个重载函数,它是函数'factorial x = case x of 0 => 1; n => n * factorial(n - 1)',在上面撒上复合糖。 – molbdnilo

回答

2

做我认为这里的真正的答案是,没有一个完全等效。然而。模板专业化很接近,但只适用于编译时,这几个限制了它的可用性。当然,我们有分支,但与其他函数式编程语言中的模式匹配相比,它的能力有限。

目前在C++模式匹配的建议:P0095r1这将使阶乘的定义如下,假设概念:

template <Integral I> 
I factorial(I n) { 
    return inspect(n) { 
     0 => 1 
     n => n * factorial(n-1) 
    }; 
} 

我不是语法完全确定,但话又说回来,它的只是目前的一个提案,所以语法本身可能会改变。

2

有这样的事情,它叫做模板专业化。基本上,除了常规模板定义之外,您可以为给定类型定义模板。你可以阅读一下here

//Main template definition 
template<typename T> 
void foo(T) { std::cout << "Some T\n"; } 

//Specialization for int 
template<> 
void foo(int) { std::cout << "Called with an int!\n"; } 

阶乘模板“功能”也使用模板特,但由于模板的性质,它只能算编译时间值(模板元编程):

template<std::size_t N> 
struct factorial { 
    static constexpr unsigned long long value = N * factorial<N - 1>::value; 
}; 

template<> 
struct factorial<0> { 
    static constexpr unsigned long long value = 1; 
} 

auto foo = factorial<10>::value; 

据我所知,在给定函数中运行时没有这样的事情(除了switch/if branches)。

0

简答:没有C++没有Haskell样式的模式匹配。另外值得一提的是,在Haskell的例子中,你只有一个函数,而不是其中的两个,但是你只是有一个更好的语法来检查输入的值。在重载中,实际上有两个或更多具有相同名称的函数,但具有不同数量或类型的参数。

更长时间/更真实的答案:类似于您建议可以通过template-metaprogramming。由于C++模板允许将值作为模板参数,而不仅仅是类型,所以实际上可以构建这样的函数。模板语言显然是图灵完备的,所以你可以实际计算一切可以用它来计算的东西。 当然,它看起来很糟糕,并导致大量的编译时间和难以理解初始化后的代码。

0

运行时分支使用if或三元运算符 <condition> ? <if-true> : <if-false>完成。

函数的重载在编译时间完成,所以这意味着如果你想根据值选择一个函数的重载,你必须在编译时严格地知道该值。

下面是编译时使用SFINAE分支的例子:

template<int n, std::enable_if_t<(n > 1), short> = 0> 
constexpr int factorial(std::integral_constant<int, n>) { 
    return n * factorial(std::integral_constant<n - 1>{}); 
} 

template<int n, std::enable_if_t<(n == 0), short> = 0> 
constexpr int factorial(std::integral_constant<int, n>) { return 1; } 

在此,注意到enable_if_t所用的条件。如果条件不满足,它将使该函数无效,并强制编译器尝试替代函数。

当然,语法不是很好。最好是将有两个运行一个单一的执行和编译时间,但是,你必须使用传统的分支:

constexpr factorial(int n) { 
    return n == 0 ? 1 : n * factorial(n - 1); 
} 
1

如果数值在编译时已知,它可以使用模板来完成

//recursively calls itself until N is 1 
template<int N> 
struct factorial<N>{enum{value = N * factorial<N-1>::value};}; 

//at which point, this will be called (stopping the recursion) 
template<> 
struct factorial<1>{enum{value = 1};}; 

如果数值在运行时才知道,这个决定必须在运行时

int factorial_recursion(int n){ 
    if(n == 1) 
    return 1; 
    else 
    return n * factorial_recursion(n - 1); 
} 
//or 
int factorial_loop(int n){ 
    int answer = 1; 
    for(int count = n; count > 1; --count) 
    answer *= count; 

    return answer; 
} 
0
int factorial(int n) 
{ 
    switch(n) 
    { 
     case 0: return 1; 
     default: return n * factorial(n - 1); 
    } 
} 
+0

我的想法是,编译器会/可能/可能会创建这个“不足之处”。 –

+0

@JasonDoege我认为C++完全是关于显性的。如果让编译器在人们的背后哄骗,我会认为是违背语言的精神。我知道像Haskell这样的人,但对我来说,语法是非常神秘的,(除了这个微不足道的情况)极难推理。我不知道也许这只是我是一个老机器代码黑客的心:) –

相关问题