【问题标题】:Recursive function returns sum, struggling to understand why?递归函数返回总和,难以理解为什么?
【发布时间】:2017-07-26 14:17:32
【问题描述】:

在审查表上给了我以下功能:

(define mystery(lambda(m n)
                 (cond
                   ((= m 0) n)
                   ((= n 0) m)
                   (#t (+ 2(mystery(- m 1)(- n 1))))
                   )))

前两个条件很简单,只是递归的otherwise 让我感到困惑。在我看来,递归将继续,直到它们都等于零,这肯定不会返回总和。谁能解释一下?

【问题讨论】:

    标签: functional-programming scheme lisp


    【解决方案1】:

    首先,让我们更好地格式化代码,看看发生了什么:

    (define (mystery m n)
      (cond ((= m 0) n)
            ((= n 0) m)
            (else (+ 2 (mystery (- m 1) (- n 1))))))
    

    现在,请记住 cond 只执行与第一个条件 true 对应的操作(从上到下),其他的被忽略。如果没有任何条件是true,则执行else 部分。要记住的重要一点是只执行一个操作。

    特别是,当mn 变为零时,您的mystery 过程将停止,而不是当两者 变为零时。当两者之一达到零时,递归开始展开,返回总和。您可以在跟踪执行时看到这一点 - 例如,在 Racket 中:

    (require racket/trace)
    (trace mystery)
    (mystery 3 2)
    
    >(mystery 3 2)
    > (mystery 2 1)
    > >(mystery 1 0)
    < <1
    < 3
    <5
    

    【讨论】:

      【解决方案2】:

      只是为了详细说明 Óscar López 的回答(我无法在评论中对此进行格式化):我发现将这些小的递归数学函数写下来通常很有用,就好像它们是数学一样:

      设m和n为自然数,则

      • n + m = n 如果 m = 0;
      • n + m = m 如果 n = 0;
      • n + m = n - 1 + m - 1 + 2;
      • 没有其他情况。

      【讨论】:

        【解决方案3】:

        我觉得最好的方法不是嵌套而是预先计算。查看我们使用零进行测试的基本情况:

        (mystery 0 2) ; ==> 2
        (nystery 3 0) ; ==> 3
        

        因此,每次至少一个参数为零时,它都会返回另一个参数。让我们尝试使用一个非零值,并记住第二秒你看到我们已经完成的值,然后再用它的结果切换它:

        (mystery 1 3)        ; ==
        (+ 2 (mystery 0 2))  ; == (we switch known value)
        (+ 2 2)                        
        ; ==> 4 
        
        (mystery 4 1)       ; == (we substitute with the expression)
        (+ 2 (mystery 3 0)) ; == (we switch known value)
        (+ 2 3) 
        ; ==> 5 
        

        因为我们知道基本情况总是返回另一个值,我们不需要预先计算它。这是这样做的:

        (mystery 3 9)                  ; == (we substitute with the expression)
        (+ 2 (mystery 2 8)             ; == (we substitute with the expression)
        (+ 2 (+ 2 (mystery 1 7)))      ; == (we substitute with the expression)
        (+ 2 (+ 2 (+ 2 (mystery 0 6))) ; == (we substitute with the expression, n, which is 6)
        (+ 2 (+ 2 (+ 2 6)))            ; == (we substitute (+ 2 6))
        (+ 2 (+ 2 8))                  ; == (we substitute (+ 2 8))
        (+ 2 10)                       ; == (we substitute (+ 2 10)
        ; ==> 12
        

        我们可以概括会发生什么。 nm 中的最低值将决定递归何时结束。在每一步,它将添加 2 并递归。因此这是一种奇特的制作方式:

        (define (double-min n m)
         (let ((vmin (min n m))
               (vmax (max n m)))
           (+ (* 2 vmin) (- vmax vmin))))
        

        这又是一种将两个数字相加的奇特方式,因为如果n &gt; m,那么2*m+(n-m) = m+m+(n-m) = m+n

        【讨论】:

        • 这个答案是错误的,很难理解。 (mystery 0 &lt;something&gt;) 应该是 &lt;something&gt;,而不是 0mystery 实现了问题标题所暗示的加法,而不是“双分钟”。
        • @jerry Mystery,就像在 OPs 代码中一样,实现加法。如果nm 为零,则结果为零。默认情况下加 2 和递归的结果,两个参数都减 1。因此,基本情况是当两者中的最小者为零时,使函数计算其参数中最小者的双倍。因此(* 2 (min n m))。使用预先计算的值进行替换是推理纯递归函数的最简单方法。
        • @Sylwester 当任一参数为 0 时,返回另一个参数,而不是 0 参数。除非我完全误解了某些东西,否则它是加法。我很确定(mystery 5 1) 会是6,而不是2。我会实际运行并验证。
        • @jerry OMG,你是对的。我没有发现基本情况返回了另一个可能不为零的参数。更新。谢谢你:-)
        【解决方案4】:
        (define mystery(lambda(m n)
                         (cond
                           ((= m 0) n)
                           ((= n 0) m)
                           (#t (+ 2 (mystery (- m 1) (- n 1))))
                           )))
        

        第一个和第二个条件很明显。

        解释第三条语句的工作原理:

        1 从 m 和 n 中取出,并在此函数之外保持为 2。 这一直持续到 m 为 0 或 n 为 0。

        【讨论】:

          【解决方案5】:

          前两种情况很明显,其中一个为0的2个数之和等于另一个数。

          在最后一种情况下,在检查参数是否为 0 之后,我们确定它们都是非 0。 假设神秘返回它的两个参数的总和,那么

          (+ 2 (mystery (- arg1 1) (- arg2 1)))
          

          将返回一个等于 (mystery arg1 arg2) 的总和,并最终在其中一个参数为 0 时停止,返回所需的结果。

          假设神秘返回其 2 个参数的总和是这里的关键,被称为 信仰的递归飞跃。 (谷歌)

          【讨论】:

            猜你喜欢
            • 2013-09-29
            • 1970-01-01
            • 1970-01-01
            • 2016-02-22
            • 1970-01-01
            • 1970-01-01
            • 2021-12-23
            相关资源
            最近更新 更多