C+模版编程实现Haskell的函数模式匹配特性

大神Bartosz Milewski在2009年写了一篇文章《What Does Haskell Have to Do with C++?》,使用C++实现Haskell函数式编程语言的一些特性。【传送门在文末】

其中有这样一段例子:

// code 1

1.templateclassfact{

2.public:

3.staticconstintvalue=n*fact::value;

4.};

5.

6.templateclassfact{// specialization for n = 0

7.public:

8.staticconstintvalue=1;

9.};

注:原文中使用的是struct关键字,这里改为class并加上了public

我猜,你没看懂。没关系,我们先跳过上面这一段有着【令人恐怖的语法】的C++模版代码。

上面的例子想干嘛呢?其实它只是想计算n的阶乘。

如果你在C语言里面学过递归,应该知道下面这段计算阶乘的递归函数

// code 2

intfact(intn){

if(==n)

return1;//0阶问题答案。0!等于1

else

return(n*fact(n-1));//问题降阶:n阶->n-1阶

}

它的效果就等于下面的代码

// code 3

intfact2(intn){ // 用 for 循环计算阶乘

intp=1;

for(inti=n;i>=1;i--)

p*=i;

returnp;

}

那么,第一段代码(code1)与第二段代码(code2)的区别在哪里呢?

区别在于,code1是在编译时(由编译器)计算的,code2是在运行时(就是代码运行的时候)计算的。

现在来解释一下code1(部分根据Bartosz Milewski文中的说法)

// code 1

/*第1行代码声明了一个类模版fact。

这个模版接受一个“非类型参数”n,

n是整数。

*/

1.templateclassfact{

2.public:

/*第3行代码声明了一个静态整型常量

成员value。而value的值是使用

递归模版表示的

*/

3.staticconstintvalue=n*fact::value;

4.};

5.

/*第6行代码是“特化”类模版fact,

也就是显式地给出某种类型参数的

类模板的一个实例的代码,而非由

编译器生成。

在这里,是给出了参数n为时模板

fact的代码。这样,编译器不会再

根据类模版fact生成n=0时的代码

关于模版特化,详见文末链接

*/

6.templateclassfact{// specialization for n = 0

7.public:

8.static const intvalue =1;

9.};

/*根据C++规范,模版特化的代码必须

放到模版声明之后。

因此上面的代码看上去好像先处理了

由n阶到n-1阶的降阶问题,然后再给

出了阶的解答

这可不像code2。code2中有if/else,

因此可以把降阶代码与阶解答代码调

换先后次序(当然if条件得改)。

*/

那么这个用模版计算阶乘的代码(类?)该怎么用呢?如下:

cout::value

其中,C++编译器会为“fact::value”这个调用匹配最合适的模版代码,也就是code1中的第6-9行代码。

如果用非零参数调用呢?

cout::value

其中,C++编译器会为“fact::value”这个调用匹配code1中的第1-4行代码。

前面blahblhaaaaaaaaaaaah讲了一大堆,其实都不是正经事儿。

正经是下面的Haskell代码:

//code 4

1.fact=1

2.fact n=n*fact(n-1)

上面两行代码定义了函数fact。fact是函数名,fact的后面、等号的前面是函数的参数。等号后面是函数体,函数体的计算结果就是fact函数的返回值。

当程序员调用【fact 8】的时候(参数是8,因为Haskell函数调用一般不像C++那样给参数加括号),Haskell会将之匹配到上面代码的第2行。这种参数匹配,是Haskell特有的函数声明与调用方式。

所以前面的code1中C++模版代码,就是在模仿code4中的Haskell代码。

下面给出一个完整的Haskell程序

moduleFactwhere

importSystem.IO

fact::Integer->Integer

fact=1

factn=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++说:你欺负人!

Bartosz Milewski文章《What Does Haskell Have to Do with C++?》的传送门:

短网址:http://t.cn/Rg6yxeW

原网址:https://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/

模版特化:

在百度百科中搜索“模版特化”

链接:https://baike.baidu.com/item/%E6%A8%A1%E6%9D%BF%E7%89%B9%E5%8C%96

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180718G02ZQT00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

同媒体快讯

扫码关注腾讯云开发者

领取腾讯云代金券