算进程是一个递???计算进程,因为他在计算的进程当中需要耗费资源来保存所需的一些情况和信息。递归的计算过程,想必大家都很熟悉,它是属于一种推迟求值的方式,不能够立即求出最终结果,每一次的计算都依赖于它的子计算状态,直到遇到边界之后,才逐层返回计算结果,最终返回到起点,求出结果。可以说,递归是从后向前推的过程,计算最后一步,也就是最终结果,也许就会需要倒数第一步的结果,那么就计算倒数第一步,以此类推,直到计算到第一步的时候,利用题设条件可以得到第一步的最终结果,然后再把计算结果逐层返回到起点,也就是计算最后一步的位置,得到最终结果。(是深度优先搜索的道理)但是如果计算的次数太多,迭代的层数太深,留给过程的栈空间很有可能会溢出,造成爆栈。
那么,根据上面的时间和空间复杂度两个指标,我们将评判这个用递归计算阶乘的计算进程的好坏。(其实计算进程就是算法,有木有?!)
我们可以看到,如果n是6,那么一共要计算12次,如果n是5那么要计算10次,所以相应的时间复杂度大致可以估算为O(N)。从(factorial 6)递归到边界最低端的时候,一共进行了6次迭代,随着n的增加,递归的次数会随n线性增长,所需的内存空间也会线性增长。所以控件复杂度应为O(N)。
综上所述,整个递归计算进程已经评价完毕,准确的说,这是一个线性递归计算进程。
算法:n!就是从1开始一直累乘到n,最终的累乘结果等于n!
根据这个算法,写出一个过程如下
从上述的过程我们可以看出,它在定义的时候,也是调用了自身。计算(factorial 6)的进算进程图示如下:

根据图示可以看出,每一次的计算状态由三个变量来维持,而且每一个状态的计算和其他状态的计算是独立的,各自状态的计算结果不依赖于其他的状态。每一次状态的计算都是干脆利落的执行,下一个状态计算所需要的资源通过参数传递,上一个状态计算完成之后就没有任何用处了,它不必为前一个或者后一个状态来保存什么有用的信息,需要的信息都根据参数传递给新的状态了。而且,在不断计算的过程中,由于把所需要的值的信息一直作为参数传递,一旦得出了最终的结果,不用像递归一样,把计算结果返回给上一个状态,而是直接返回给一开始的调用者。过程的调用都是在栈空间来开辟内存的,根据以上计算特点,上一个状态计算结束之后可以立即销毁之前所用的栈空间,避免了栈溢出。
这样的计算进程,很容易就可以看出来,时间复杂度O(N),空间复杂度,因为每次计算只需要保存三个变量,它不随n的变化而变化,则为O(1),常量级。不仅仅是这个计算过程这样,计算机当中所有的计算的空间复杂度都应该在常数级别下完成,因为内存空间是有限的。
上述计算进程被称为迭代,如果单就这个实例来说,应该是线性迭代进程。
一般的编程语言都是通过循环等一些复杂的结构来描述迭代的,但是在scheme中却可以用递归的形式来描述迭代。
观察上面两个求阶乘的过程,我们可以看到。虽然两者都是调用了自身,用了递归的形式来定义整个过程,表面上都属于递归调用,但是,『递归』版本中,在最后的递归调用之后,还需要进行乘n的操作才算作是完成了此次计算,而在 [迭代]版本中,递归的调用属于最后一个方法,末尾的递归调用执行完,此次的计算也就执行完了,属于该过程的最后一个操作,这就是”尾递归”。尾递归相对于正常的递归来说,它的递归调用处于过程最后,之前过程计算积累的信息对于接下来的这次递归调用的最终结果没有任何影响,那么本次过程调用存储在栈内的信息就可以完全清除。把空间留给之后的递归过程使用。所以说,这意味着如果使用尾递归的方式,是可以实现无限递归的。
至于把正常递归转化为尾递归的方法,我觉得比较直接的做法就是,正常递归是从后向前考虑,如果写尾递归,那么就把问题从前向后考虑,并且把所需的信息当做参数传递。
一般来讲,虽然在过程定义上,看起来是递归的,但是实际的计算进程的原理有可能是迭代的,也有可能是递归的,因为,迭代的计算是可以用尾递归来进行定义的。