函数递归
Factorial称之为阶乘,维基百科是这样描述的“一个正整数的阶乘是所有小于及等于该数的正整数的积,并且有0的阶乘为1。自然数n的阶乘写作n!。”
递归就是程序自己调用自己( recursion),简单说来就是不断地重复执行同样的代码来解决问题。
而阶乘函数是递归(Haskell)函数典型示例。
一般来说,一个递归函数的定义有两个部分。首先,至少要有一个底线,就是一个简单的线,越过此处,递归会停止(换言之,此时函数会直接返回值,而不会继续“递归般地”调用自身。底线保证了此函数必定结束。其次,是更一般的递归区,若参数在此范围内就会以某种形式调用自身。
阶乘函数
数学上,尤其是组合数学,有一个相当常用的函数叫做阶乘(Factorial)。
阶乘函数的参数是一个自然数,它会返回1与此数之间所有数的乘积。比如,6的阶乘是 1 × 2 × 3 × 4 × 5 × 6 = 720 。这相当有趣,因为对我们来说,它可以用一种递归函数来表示。
首先,我们比较相邻两个数的阶乘。
例子: 相邻数的阶乘
Factorial of 6 = 6 × 5 × 4 × 3 × 2 × 1
Factorial of 5 = 5 × 4 × 3 × 2 × 1
明显可以看出6的阶乘和5的阶乘有关系。6的阶乘就是6 × (5的阶乘)。让我们来看另一个例子。
确实,我们可以看出任何数字的阶乘就是此数字乘以比此数小1的数的阶乘。除了一个例外:0的阶乘,我们不会把0和-1的阶乘相乘。事实上,我们只是认为0的阶乘是1(我们就是这样定义的,怎么着了,你不同意?所以,0是此处递归的底线,当我们遇到0的时候,我们会立刻说答案是1,不需递归。我们可以把阶乘函数的定义简述如下。
0的阶乘是1。
任何数的阶乘都是此数乘以比此数小一的数的阶乘。
这个在Haskell中表示为:
例子: 阶乘函数
factorial 0 = 1
factorial n = n * factorial (n-1)
这就定义了一个新的叫做factorial的函数。第一行表示0的阶乘是1,第二行表示任意别的数n的阶乘等于n乘以 n-1的阶乘。注意那个把n-1括起来的括弧:没有这个括弧代码就会被解析称为(factorial n) - 1;函数行为的优先级是很高的,会最先执行。
我们可以看出乘法如何贯穿整个递归过程。
注意,我们最终的式子中1出现了两次,因为底线是0而不是1;不过这造不成什么错误,因为乘1还会是原来的数。如果我们想让 factorial在1处停下也办得到,但0做底线符合常理,而且会很实用。
还有一点需要注意的:两个声明(一个是factorial 0的声明,一个是factorial n的声明)的顺序很重要。Haskell会先匹配第一个句子,来决定函数的定义。所以,如果我们把两句声明掉个,第一个n语句将会匹配任何数(包括0)。所以语句 factorial 0
的执行过程是去匹配情况n,于是factorial 0的结果将会是0 * factorial (-1),然后就会没完没了。无限循环不是我们想要的。一般来说:特殊情况应该置于前,一般情况置于后。