Вопрос

I'm trying to expand a simple fibonacci function, and I need to use the values for each term more than once. So, I figured I'd use let to hold onto the values. But, I'm not getting what I think I should out of the function.

Here is the original fib function:

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

Here is my attempt at doing the same thing, but with 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))))

Results:

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

Thanks!

Это было полезно?

Решение

You made a typo:

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

You mean n.

Другие советы

You mistyped your base case. In the first version you had:

(if (< n 2)
      n

But then in your latter version you wrote:

(if (< n 2)
      0

So just change 0 to n.

Your let is not really doing anything. You are still doing all of the extra calculations. Just because you define f1 as (fib-with-let (- n 1)) doesn't mean you won't compute the fib of n-1 again. f2 does not use f1. If you wanted f2 to see f1 you would use let*. However, even this is not really what you want.

As evidence of this, here are the running times for fib(35) and 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

What you really want to do to avoid extra computations is use dynamic programming and recurse in a bottom-up fashion.

What you want is the following code:

(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

As you can see, you can do the first 150,000 fibs in a third of the time the naive approach takes.


Since it looks like you are confused about what let does let me illustrate better:

When you say:

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

What you are saying is, let a be 1, and b be 2, add them together. If you instead said:

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

Can you guess what you'd get? Not 3. It would be blow up with expand: unbound identifier in module in: a

In simple let, your assignments cannot see each other. If you wanted to write the above you would have to use let*:

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

That would give you the 3 you expect. let* essentially expands to:

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

What you thought you were doing with the lets is called memoization. It's a technique where you store intermediate values so you don't have to repeat them. Let, however, does not do that for you.

Although your problem is a typo in your fib-with-let function, in its simplest form, let is "syntatic-sugar" for an anonymous lambda followed by the arguments that are then evaluated and passed to the lamba, which is then evaluated and a final value returned. So

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

would be re-written without let to look like

((lambda (f1 f2) (+ f1 f2))(fib-with-let (- n 1))(fib-with-let (- n 2)))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top