在方案中创建尾递归幂函数

时间:2012-05-17 11:39:00

标签: recursion scheme tail-recursion

我想知道你如何在方案中实现尾部回复功率函数?

我已为计划定义了递归:

(define power-is-fun
  (lambda (x y)
    (cond [(= y 0)
           1]
          [(> y 0)
           (* (power-is-fun x (- y 1)) x)])))

但我无法弄清楚另一个应该是怎样的。

3 个答案:

答案 0 :(得分:5)

答案类似,您只需将累积结果作为参数传递:

(define power-is-fun
  (lambda (x y acc)
    (cond [(= y 0)
           acc]
          [(> y 0)
           (power-is-fun x (- y 1) (* x acc))])))

这样调用它,注意acc的初始值是1(你可以为此构建一个辅助函数,所以你不必记住传递1 }每次):

(power-is-fun 2 3 1)
> 8

将递归过程转换为尾递归的一些通用指针:

  • 向函数添加一个额外参数以保存到目前为止累积的结果
  • 在第一次调用过程时传递累加器的初始值,通常这与您在“正常”(非尾递归)递归中在基本情况下返回的值相同。
  • 在递归的基本情况下返回累加器
  • 在递归步骤中,使用新值更新累积结果并将其传递给递归调用
  • 最重要的是:当调用递归时,请务必将其称为最后一个表达式,而不执行“额外工作”。例如,在原始代码中,您在调用power-is-fun之后执行了乘法,而在尾递归版本中,对power-is-fun的调用是在退出之前发生的最后一件事程序

答案 1 :(得分:2)

顺便说一句,有一种更快的方法来实现整数取幂。而不是一次又一次地x乘以y次(这使得它成为O(y)),有一种方法是时间复杂度为O(log y):

(define (integer-expt x y)
  (do ((x x (* x x))
       (y y (quotient y 2))
       (r 1 (if (odd? y) (* r x) r)))
      ((zero? y) r)))

如果您不喜欢do(我知道的Schemers数量很多),这里有一个明确尾部recurses的版本(当然,您也可以使用名为let编写它:

(define (integer-expt x y)
  (define (inner x y r)
    (if (zero? y) r
        (inner (* x x)
               (quotient y 2)
               (if (odd? y) (* r x) r))))
  (inner x y 1))

(顺便说一句,任何体面的Scheme实现都应该将两个版本宏扩展到完全相同的代码。另外,就像Óscar的解决方案一样,我使用累加器,只在这里我称之为r(对于“结果” “)。)

答案 2 :(得分:1)

Óscar的建议清单非常好。

如果您想更深入地处理迭代(又称线性递归)和树递归过程,那么请不要错过SICP中的优秀处理:

http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2