编译器/解释器对Scheme中的阶乘函数进行的尾部调用优化

时间:2018-08-27 11:46:01

标签: scheme tail-recursion

是否可以由编译器/解释器针对以下给出的阶乘函数执行尾部调用优化?

(define factorial
  (lambda (x)
    (if (= x 0)
        1
        (* x (factorial (- x 1))))))

我想对此做一个简短的解释。

从下面@rsm的评论中,我了解程序应该写成这样:

(define fact
  (lambda (x accumulator)
    (if (<= x 1)
    accumulator
    (fact (- x 1) (* x accumulator)))))

(define factorial
  (lambda (x)
    (fact x 1)))

或者类似这样的东西:

(define factorial
  (lambda (n)
    (let fact ([i n] [a 1])
      (if (= i 1)
      a
      (fact (- i 1) (* a i))))))

2 个答案:

答案 0 :(得分:1)

首先,一个术语问题:作为程序员,您不能“执行”尾部调用优化。 (这是“尾部调用优化”对该属性使用不好的名称的原因之一。)在这种情况下,“优化”由评估者完成;具体来说,正确的Racket或Scheme评估程序或任何适当的尾调用语言都可以保证:它承诺不要在某些类型的程序上使用无限制的内存。具体来说,仅进行“尾部调用”的程序。

因此,您真正要问的是如何将程序转换为仅进行尾部调用的程序。此处的关键是了解尾调用,并将程序转换为累加器样式。而且,在这一点上,我将参考Alexis King's answer中出现的精彩讨论。

答案 1 :(得分:-1)

您似乎正在尝试计算x!

您使用输入x将阶乘定义为 lambda (此处它是可读的伪代码,而不是Scheme中的前缀语法):

factorial (x) = {  
  (x=0) -> 1 // end condition  
  (x<>0) -> x * fact(x-1) // 'fact' should be 'factorial' AFAIK   
} 

或换句话说:

factorial(x) * factorial(x-1) * factorial((x-1)-1) ...factorial(0) => x!

以下已是尾递归(递归是最后一次调用是递归):

factorial (x , sum(1)) = {  
  (x=0) -> sum // end condition  
  (x<>0) -> fact(x-1 ,  sum(x*sum)) // 'fact' should be 'factorial' AFAIK   
} 

返回代码,应类似于:

(define factorial
  (lambda (x , sum=1)
    (if (= x 0)
      sum
      (fact (- x 1) (* x sum)))))