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

Posted AlohaWorld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的阶乘。

 

如果你在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

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

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

   编译器生成。

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

  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阶的解答

  

   这可不像code2code2中有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)

 

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

 

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

 

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

 

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

moduleFactwhere
import
System.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 


HaskellC++说:我能算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

 


以上是关于C++模版编程实现Haskell的函数模式匹配特性的主要内容,如果未能解决你的问题,请参考以下文章

Haskell代码编程

函数返回与函数参数中的 Haskell 模式匹配

C++——函数模版与类模版

C++——函数模版与类模版

用函数式语言推箱子

C++认识模版函数