C++模版編程實現Haskell的函數模式匹配特性[圖]

C++模版編程實現Haskell的函數模式匹配特性[圖]:
大神 Bartosz Milewski 在2009年寫了一篇文章《What Does Haskell Have to Do with C++?》,使用C++實現Haskell函數式編程語言的一些特性。【傳送門在文末】
其中有這樣一段例子:
// code 1
1.template<int n>class fact {
2.public:
3.     staticconstint value = n * fact<n -1>::value;
4.};
5.
6.template<>class fact<0>{// specialization for n = 0
7.public:
8.     staticconstint value =1;
9.};
注:原文中使用的是struct關鍵字,這裏改成class並加上了public
我猜,你沒看懂。不要緊,咱們先跳過上面這一段有着【使人恐怖的語法】的C++模版代碼。
上面的例子想幹嗎呢?其實它只是想計算n的階乘。html

C++模版編程實現Haskell的函數模式匹配特性[圖]
若是你在C語言裏面學過遞歸,應該知道下面這段計算階乘的遞歸函數
// code 2
int fact(int n){
if(0== n )
return1;  //0階問題答案。0! 等於1
else
return( n * fact( n -1)); //問題降階:n階->n-1階
}
它的效果就等於下面的代碼
// code 3
int fact2(int n){ // 用 for 循環計算階乘
int p =1;
for(int i=n; i >=1; i--) 
p *= i;
return p;
}
那麼,第一段代碼(code1)與第二段代碼(code2)的區別在哪裏呢?
區別在於,code1是在編譯時(由編譯器)計算的,code2是在運行時(就是代碼運行的時候)計算的。
如今來解釋一下code1 (部分根據Bartosz Milewski文中的說法)
// code 1
/* 第1行代碼聲明瞭一個類模版 fact。
這個模版接受一個「非類型參數」n,
n是整數。
*/
1.template<int n>class fact {
2.public:
/* 第3行代碼聲明瞭一個靜態整型常量
成員 value。而 value 的值是使用
遞歸模版表示的
*/
3.     staticconstint value = n * fact<n -1>::value;
4.};
5.
/* 第6行代碼是「特化」類模版fact,
也就是顯式地給出某種類型參數的
類模板的一個實例的代碼,而非由
編譯器生成。
在這裏,是給出了參數n爲0時模板
fact的代碼。這樣,編譯器不會再
根據類模版fact生成n=0時的代碼
關於模版特化,詳見文末連接
*/
6.template<>class fact<0>{// specialization for n = 0
7.public:
8.    static const int value = 1;
9. };
/* 根據C++規範,模版特化的代碼必須
放到模版聲明以後。
所以上面的代碼看上去好像先處理了
由n階到n-1階的降階問題,而後再給
出了0階的解答
這可不像code2。code2中有if/else,
所以能夠把降階代碼與0階解答代碼調
換前後次序(固然if條件得改)。
*/
那麼這個用模版計算階乘的代碼(類?)該怎麼用呢?以下: 
cout <<"Factorial of 0 = "<< fact<0>::value << endl;
其中,C++編譯器會爲「fact<0>::value」這個調用匹配最合適的模版代碼,也就是code1中的第6-9行代碼。
若是用非零參數調用呢?
cout <<"Factorial of 8 = "<< fact<8>::value << endl;
其中,C++編譯器會爲「fact<8>::value」這個調用匹配code1中的第1-4行代碼。
前面blahblhaaaaaaaaaaaah講了一大堆,其實都不是正經事兒。
正經是下面的Haskell代碼:
//code 4
1. fact 0=1
2. fact n = n * fact (n -1)
上面兩行代碼定義了函數fact。fact是函數名,fact的後面、等號的前面是函數的參數。等號後面是函數體,函數體的計算結果就是fact函數的返回值。
當程序員調用【fact 8】的時候(參數是8,由於Haskell函數調用通常不像C++那樣給參數加括號),Haskell會將之匹配到上面代碼的第2行。誰動了個人奶酪讀書筆記(http://www.simayi.net/dushubiji/6208.html)摘抄好詞好句及感悟賞析,這種參數匹配,是Haskell特有的函數聲明與調用方式。
因此前面的code1中C++模版代碼,就是在模仿 code4 中的Haskell代碼。
下面給出一個完整的Haskell程序
moduleFactwhere
importSystem.IO
fact::Integer->Integer
fact0=1
fact n = n * fact (n-1)
main::IO()
main=do
putStrLn $"8!  = "++ show (fact 8)
putStrLn $"88! = "++ show (fact 88)
上面的代碼輸出結果是:
8!  = 40320
88! =185482642257398439114796845645546284380220968949399346684421580986889562184028199319100141244804501828416633516851200000000000000000000 
Haskell對C++說:我能算88!,你行嗎?
C++說:你欺負人!程序員

相關文章
相關標籤/搜索