试图在计划中理解“让”

时间:2011-06-16 18:48:41

标签: recursion scheme fibonacci let

我正在尝试扩展一个简单的斐波纳契函数,我需要多次使用每个术语的值。所以,我想我会使用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

谢谢!

4 个答案:

答案 0 :(得分:7)

你写了一个错字:

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

您的意思是n

答案 1 :(得分:6)

您错误输入了基本案例。在第一个版本中你有:

(if (< n 2)
      n

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

(if (< n 2)
      0

所以只需将0更改为n

答案 2 :(得分:2)

你的让步并没有真正做到。你还在做所有额外的计算。仅仅因为你将f1定义为(fib-with-let (- n 1))并不意味着你不会再次计算n-1的纤维。 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。这是一种存储中间值的技术,因此您无需重复这些值。但是,让我们不要那样做。

答案 3 :(得分:0)

虽然你的fib-with-let函数中的问题是一个拼写错误,但最简单的形式是let是一个匿名lambda的“syntatic-sugar”,后跟参数然后被评估并传递给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)))