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