あなたのレットは実際には何もしていません。あなたはまだすべての余分な計算を行っています。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
余分な計算を避けるために本当にやりたいことは、動的計画法を使用し、ボトムアップ方式で再帰することです。
必要なのは次のコードです。
(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
ご覧のとおり、ナイーブなアプローチにかかる時間の3分の1で、最初の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)))
あなたがレットでやっていたと思ったことは、メモ化と呼ばれます。これは、中間値を保存して、それらを繰り返す必要がないようにする手法です。しかし、あなたのためにそれをしないようにしましょう。