【问题标题】:Tail-Recursive Power Function in SchemeScheme中的尾递归幂函数
【发布时间】:2013-10-05 15:18:51
【问题描述】:

我在方案中编写尾递归幂函数时遇到问题。我想使用辅助函数编写函数。我知道我需要一个参数来保存累积值,但在那之后我被卡住了。我的代码如下。

(define (pow-tr a b)
(define (pow-tr-h result)
  (if (= b 0)
     result
     pow-tr a (- b 1))(* result a)) pow-tr-h 1)

我编辑了我的代码,现在它可以工作了。如下:

(define (pow-tr2 a b)
 (define (pow-tr2-h a b result)
  (if (= 0 b)
    result
    (pow-tr2-h a (- b 1) (* result a))))
 (pow-tr2-h a b 1))

谁能解释一下为什么辅助函数应该和主函数有相同的参数。我很难想出为什么这是必要的。

【问题讨论】:

    标签: function recursion scheme tail-recursion tail


    【解决方案1】:

    说“辅助函数应该与主函数具有相同的参数”是不正确的。您只需要在每次迭代中传递将要更改 的参数 - 在示例中,是指数和累积结果。例如,这将在不将基数作为参数传递的情况下正常工作:

    (define (pow-tr2 a b)
      (define (pow-tr2-h b result)
        (if (= b 0)
            result
            (pow-tr2-h (- b 1) (* result a))))
      (pow-tr2-h b 1))
    

    之所以有效,是因为内部辅助过程可以“看到”外部主过程中定义的a 参数。而且因为基础永远不会改变,我们不必传递它。要了解更多信息,请查看精彩的 SICP 书中标题为“内部定义和块结构”的部分。

    既然您正在使用帮助程序,最好开始使用named let,这是一种非常方便的语法,无需显式编码内部程序即可编写帮助程序。上面的代码等价于:

    (define (pow-tr2 a b)
      (let pow-tr2-h [(b b) (result 1)]
        (if (= b 0)
            result
            (pow-tr2-h (- b 1) (* result a)))))
    

    【讨论】:

    • @JoshuaTaylor 是的,这似乎是个好时机。我用你的建议更新了我的答案
    【解决方案2】:

    即使名称相同,参数也不同。如果您深入了解解释器正在做什么,您会看到“a”被定义了两次。一次用于本地范围,但它仍然记住外部范围上的“a”。当解释器调用一个函数时,它会尝试将参数的值绑定到形式参数。

    您像在algol 家族语言中那样通过相当可变的状态传递值的原因是,通过不改变状态,您可以使用替换模型来推理过程的行为。在任何时候使用参数调用的相同过程将产生与从其他任何地方使用相同参数调用相同的结果。

    在纯粹的函数式风格中,值永远不会改变,而是你不断地用新的值调用函数。编译器应该能够在一个紧密循环中编写代码来更新堆栈上的值(尾调用消除)。通过这种方式,您可以更多地担心算法的正确性,而不是充当人工编译器,这实际上是一种非常低效的机器任务配对。

    【讨论】:

      【解决方案3】:
      (define (power a b)
        (if (zero? b)
         1
        (* a (power a (- b 1)))))
      
      
      (display (power 3.5 3))
      

      【讨论】:

      • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
      • 这个解决方案不是尾递归的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-10
      • 2018-07-28
      • 1970-01-01
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多