【问题标题】:Trying to understand "let" in scheme试图理解方案中的“让”
【发布时间】:2011-09-16 15:30:31
【问题描述】:

我正在尝试扩展一个简单的斐波那契函数,并且我需要多次使用每个术语的值。所以,我想我会使用let 来保留这些值。但是,我没有得到我认为应该从函数中得到的东西。

这里是原来的fib函数:

(define (fib n)
  (if (< n 2)
      n
      (+ (fib (- n 1)) (fib (- n 2)))))

这是我做同样事情的尝试,但使用let

(define (fib-with-let n)
  (if (< n 2)
      0
      (let ((f1 (fib-with-let (- n 1)))
            (f2 (fib-with-let (- n 2))))
        (+ f1 f2))))

结果:

> (fib 10)
55
> (fib-with-let 10)
0

谢谢!

【问题讨论】:

    标签: recursion scheme fibonacci let


    【解决方案1】:

    你打错了:

    (if (< n 2)
        0
        ...)
    

    你的意思是n

    【讨论】:

      【解决方案2】:

      您打错了基本情况。在您拥有的第一个版本中:

      (if (< n 2)
            n
      

      但是在您的后一个版本中,您写道:

      (if (< n 2)
            0
      

      所以只需将0 更改为n

      【讨论】:

        【解决方案3】:

        尽管您的问题是您的 fib-with-let 函数中的拼写错误,但在最简单的形式中,let 是匿名 lambda 的“语法糖”,后跟参数,然后评估并传递给 Lamba,即然后评估并返回最终值。所以

        (let ((f1 (fib-with-let (- n 1)))
              (f2 (fib-with-let (- n 2))))
                (+ f1 f2))
        

        将在没有let 的情况下重写为看起来像

        ((lambda (f1 f2) (+ f1 f2))(fib-with-let (- n 1))(fib-with-let (- n 2)))
        

        【讨论】:

          【解决方案4】:

          你的 let 并没有真正做任何事情。您仍在进行所有额外的计算。仅仅因为您将 f1 定义为 (fib-with-let (- n 1)) 并不意味着您不会再次计算 n-1 的 fib。 f2使用f1。如果您希望 f2 能够查看 f1,您可以使用 let*。然而,即使这也不是你真正想要的。

          以下是运行时间for fib(35)fib-with-let(35)

          (time (fib 35))
          cpu time: 6824 real time: 6880 gc time: 0
          (time (fib-with-let 35))
          cpu time: 6779 real time: 6862 gc time: 0
          

          您真正想要避免额外计算的做法是使用 dynamic programming 并在 bottom-up fashion 中递归。

          你想要的是下面的代码:

          (define (dynprog-fib n)
            (if (< n 2)
                n
                (dynprog-fib-helper 1 1 2 n)))
          
          (define (dynprog-fib-helper n1 n2 current target)
            (if (= current target)
                n2
                (dynprog-fib-helper n2 (+ n1 n2) (add1 current) target)))
          
          (time (dynprog-fib 35))
          cpu time: 0 real time: 0 gc time: 0
          (time (dynprog-fib 150000))
          cpu time: 2336 real time: 2471 gc time: 644
          

          如您所见,您可以在幼稚方法所用时间的三分之一内完成前 150,000 个谎言。


          因为您似乎对让我更好地说明什么感到困惑:

          当你说:

          (let ((a 1)
                (b 2))
            (+ a b))
          

          您的意思是,让 a 为 1,b 为 2,将它们加在一起。 如果你说:

          (let ((a 1)
                (b (+ a 1))
            (+ a b))
          

          你能猜出你会得到什么吗?不是 3. 它会被 expand: unbound identifier in module in: a 炸毁

          在简单的let 中,您的作业不能看到彼此。 如果您想编写以上内容,则必须使用let*

          (let* ((a 1)
                (b (+ a 1))
            (+ a b))
          

          这会给你你所期望的 3。 let* 本质上扩展为:

          (let ((a 1))
               (let ((b (+ a 1)))
                    (+ a b)))
          

          您认为您使用 let 执行的操作称为 memoization。这是一种存储中间值的技术,因此您不必重复它们。但是,Let 不会为您做到这一点。

          【讨论】:

          • 很棒的答案,我喜欢你的透彻。
          猜你喜欢
          • 1970-01-01
          • 2011-08-24
          • 1970-01-01
          • 2020-07-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-03-21
          相关资源
          最近更新 更多