2016-09-14 62 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這樣的人,但對我來說,語法是非常神祕的,(除了這個微不足道的情況)極難推理。我不知道也許這只是我是一個老機器代碼黑客的心:) –

相關問題