`

用递归计算阶乘咋不行呢?

阅读更多

    读《代码大全2》,已经读了一半,喘口气。总结八个字:百科全书,受益匪浅。小到一个赋值语句、一个循环的编写,大到需求分析、架构设计,无所不包,看后 半部分目录,更是扯到了重构、软件工艺、程序员的性格特征这样的话题。恰好手边的工作暂时比较有闲,可以实践下“创建高质量的代码”中的部分建议,晚上读 书,第二天就重构,乐在其中。这一部分中对设计、子程序、类、变量、语句的处理建议,可能你平常已经在这么做,可作者这么精辟地概括出来让人叹服,而有些 地方是你平常绝对很少注意的,特别是在变量和三种常见控制语句的处理上。
  
    说说我认为是缺点的地方,就是作者貌似对函数式语言了解很少,举的例子全部用的是广泛应用的静态语言(c/c++,java,vb)。例如作者有这么一句话:如果为我工作的程序员用递归去计算阶乘,那么我宁愿换人。作者对递归的态度相当谨慎,这在静态命令式语言中显然是正确的,但是在函数式语言中,由于有尾递归优化的存在,递归反而是最自然的形式,况且我打心里认为递归更符合人类思维。请注意,在FP中只有尾递归的程序才是线性迭代的,否则写出来的递归可能是线性递归或者树形递归,两种情况下都可能导致堆栈溢出并且性能较差。

scheme写阶乘:

(define (fac n)
  (if (= 1 n)
      1
      (* n (fac (- n 1)))))
  显然这个版本不是尾递归,计算过程是一个线性递归过程,计算(fac 4)的过程如下:

(* 4 (fac 3))
(* 4  (3 * (fac 2)))
(* 4  (3 * (* 2 (fac 1))))
(* 4  (3 * (* 2 1)))
(* 4  (3 * 2))
(* 4 6)
24
    因为解释器是采用应用序求值,需要将表达式完全展开,然后依次求值,在这个过程中,解释器内部需要保存一条长长的推迟计算的轨迹。
改写成一个尾递归版本:


(define (fac n)
  (define (fac-iter product n)
    (if (= 1 n)
        product
        (fac-iter (* n product) (- n 1))))
  (fac-iter 1 n))

 我们来看看它的计算过程:


(fac-iter 1 4)
(fac-iter 4 3)
(fac-iter 12 2)
(fac-iter 24 1)
24


可以看到,在这个过程中,解释器不需要保存计算轨迹,迭代的中间结果通过product变量来保存,这是一个线性迭代的计算过程。
最后再看一个斐波拉契数列的例子:


(define (fib n)
  (cond ((= n 0) 0)
            ((= n 1) 1)
            (else
                 (+ (fib (- n 1))  (fib (- n 2))))))
  这个计算过程展开是一个树形递归的过程(为什么说是树形?展开下计算过程就知道),改写为线性迭代:
(define (fib n)
  (define (fib-iter a b count)
     (if (= count 0)
         b
         (fib-iter (+ a b) a (- count 1))))
   (fib-iter 1 0 n))
 
     上述的内容在sicp第一章里有更详细和深入的讨论。
10
2
分享到:
评论
3 楼 guile 2008-03-20  
恩,所以人家Code Complete指的也应该是计算过程,所以说的和你是一个意思,所以难道你还不是挑刺啊:)
2 楼 dennis_zane 2008-03-19  
我没有想偏,我也没有说尾递归是fp的专利,我更强调线性迭代和所谓线性递归是指计算过程。呵呵,这文章不是挑刺。
1 楼 guile 2008-03-19  
人家说的应该就是计算过程,而不是调用的形式,这本来就不是一本介绍开发语言的书,也不可能讨论形式上这些属于语言细节的问题。

你的理解是对的,但是可能想偏了,回想一下SICP相关章节中的一些习题,也会要求你分别用递归和迭代的方式去实现,都是指的计算过程。

另外尾递归也不是FP的专利,只要愿意,C也可以实现一个支持尾递归的编译器来。事实上,我记得IBM JDK141就早已支持尾递归的写法了。

相关推荐

    使用递归计算阶乘

    java中使用递归方法计算阶乘的代码示例

    java递归实现 阶乘

    在Java中,我们可以创建一个名为`factorial`的方法,该方法接受一个整数n作为参数,然后通过递归调用来计算阶乘。以下是具体实现: ```java public class Factorial { public static void main(String[] args) { ...

    python非递归方式计算阶乘(循环)

    python非递归方式计算阶乘(循环)

    VB编写的递归求阶乘

    这个函数会接受一个整数作为参数,然后根据递归定义计算阶乘。递归的基本思想是将大问题分解为小问题,直到小问题简单到可以直接求解。在阶乘的例子中,我们利用0的阶乘是1(0! = 1)这一基础,以及n的阶乘等于n乘以...

    VB 递归求阶乘

    总结一下,VB递归求阶乘的关键在于定义一个自调用的函数,通过不断缩小问题规模,最终达到基本结束条件。这个过程中,递归函数的结束条件、递归调用以及返回值的计算是三个核心要素。通过学习和实践这个例子,你可以...

    递归求和与计算阶乘1

    本文将深入探讨递归求和与计算阶乘这两个主题。 首先,我们来看递归求和。递归求和是通过递归子程序实现的,它通常包含一个终止条件以避免无限循环。以`CalcSum`为例,这个过程接受一个整数`n`作为输入参数,并计算...

    利用递归算法求阶乘(VB6.0代码编写)

    在VB6.0中,我们可以创建一个函数来实现递归计算阶乘。下面我们将详细介绍如何编写这个函数: 首先,我们需要创建一个新的标准模块,在该模块中定义一个名为`Factorial`的函数。函数的参数为一个正整数n,返回值...

    易语言递归算法求阶乘

    在这个场景中,我们讨论的是使用易语言实现的递归算法来计算阶乘。 阶乘是一个数学概念,表示一个正整数n的所有小于等于n的正整数的乘积,通常表示为n!。例如,5!(5的阶乘)等于5×4×3×2×1=120。递归算法求阶乘...

    c语言递归求阶乘源码

    - `main()` 函数是程序的入口点,负责接收用户输入并调用 `jc()` 函数来计算阶乘。 - `jc()` 函数实现了递归求阶乘的功能。 2. **主函数 main()**: - 首先定义了四个整型变量 `n`, `j`, `i` 和一个整型数组 `...

    python递归方式计算阶乘

    python递归方式计算阶乘

    递归法写阶乘

    **非递归方法**:也可以使用循环结构(如for循环)来计算阶乘,这种方式不会导致栈溢出,但可能不如递归直观。 #### 递归与迭代的对比 - **递归**:通常代码更简洁、逻辑更清晰;但可能效率较低,且可能导致栈溢出...

    利用递归算法求阶乘(VB6.0源代码)利用递归算法求阶乘

    在这个主题中,我们将深入探讨如何使用递归算法在VB6.0(Visual Basic 6.0)中计算阶乘。VB6.0是Microsoft开发的一款经典可视化编程环境,用于创建Windows应用程序。 阶乘是一个数学概念,表示一个正整数n的所有...

    用python递归方式实现阶乘计算

    (3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低,所以一般不提倡用递归算法设计程序。 (4)在递归调用的过程中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出等。

    C语言递归方法求阶乘

    C语言练习程序,采用递归方法求阶乘.调用子函数实现

    基础算法-python递归求阶乘和

    【基础算法】-python递归求阶乘和阶乘:是指从1到n的连续自然数相乘的积。负数没有阶乘。递归:函数作为一种代码封装,除了被其他程序正常调用外...# 本程序:根据用户输入的整数n,计算并输出n的阶乘和# 递归实现阶乘

    Java如何利用递归计算出阶乘.rar

    在编程领域,阶乘是一个非常基础且...总的来说,Java利用递归计算阶乘是一种直观而简洁的方法,但它同时也涉及到递归函数的基本原理和递归深度限制的问题。理解这些知识点对于深入学习Java和计算机科学是非常有帮助的。

    C#递归计算求阶乘和求年龄实例源码

    C#递归计算求阶乘和求年龄实例源码 1、n!=n*(n-1)*(n-2)*......*3*2*1 n!=n*(n-1)! 2、 趣味问题——年龄。有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个...

    递归算法求阶乘.rar

    递归算法为计算阶乘提供了一种简洁而直观的方法。 递归算法的基本思路是将大问题分解为小问题,直至问题变得足够简单可以直接求解。在求阶乘的过程中,我们可以设定以下两个基本条件: 1. **基本情况**:0的阶乘...

    [Java算法设计]-递归阶乘.java

    文档中涵盖了递归阶乘的基本概念,包括如何使用递归计算阶乘以及如何在Java中实现递归阶乘。此外,文档还包括一个逐步指南,介绍如何在Java中实现递归阶乘的代码,包括详细的代码示例和实现细节。 文档还涵盖了高级...

    利用递归算法求阶乘(VB6.0源代码)

    3. **递归调用**:在基础情况之外,我们使用递归调用计算阶乘。这涉及到函数调用自身,传入比当前n值小1的数。然后,函数返回上一层调用的结果乘以n。 ```vb Factorial = n * Factorial(n - 1) ``` 将这些部分组合...

Global site tag (gtag.js) - Google Analytics