学習ログ#4 delay と force
4個目の記事なので初投稿です。
programming languages part A を終えて、part B でracket 書いてます。
遅延評価の話が出てきたので、それのメモです。
遅延評価とは値が必要になるまで、計算しないという計算方法です。
重い計算があるときに、必要ないときは計算しないようにしたり。メモ化して、一度計算したら計算結果を返すようにしたり。計算のコストを減らせます。
例えば以下のようなの関数で、掛け算をすると
; 掛け算. 第二引数が引数なしの関数
(define (my-multi x y-thunk)
(cond [(= x 0) 0]
[(= x 1) (y-thunk)]
[else (+ (y-thunk) (my-multi (- x 1) y-thunk))]))
; 3 秒かかる 足し算。
(define (slow-add x y)
(begin (sleep 3)
(+ x y)))
; #1 0 * 7の掛け算
(my-multi 0 (lambda () (slow-add 3 4)))
; #2 3 * 7 の掛け算
(my-multi 3 (lambda () (slow-add 3 4)))
#1の場合は slow-addが呼ばれないので、すぐに0 が帰ってきます。
遅延評価のおかげで、(y-thunk)に処理がいくまで、関数が展開されないためです。
#2の場合はslow-addが3回呼ばれるので、9秒かかってしまいます。
#2の問題はpromiseによって解決できます。
; promise
(define (my-delay th)
(mcons #f th))
(define (my-force p)
(if (mcar p)
(mcdr p)
(begin (set-mcar! p #t)
(set-mcdr! p ((mcdr p)))
(mcdr p))))
このdelayとforceを組み合わせると、1回目のみ計算して、2回目からは保存した計算結果を返すことが出来ます。メモ化といっしょです。
my-multiの例であれば、
; 200 * 7 の掛け算
(my-multi 200
(let ([p (my-delay (lambda () (slow-add 3 4)))])
(lambda () (my-force p))))
以前の方法であれば、200*3= 600秒かかる処理ですが、計算結果を保持して再計算をしないので、3秒の処理になります。また、x=0のときは、遅延評価のおかげで計算がよばれないので、すぐに結果を得られます。
このように、遅延評価やdelay forceを使うと不用な計算や再計算をしないで済むので高速になります。
以上です。また気がむいたら書きます。
この記事が気に入ったらサポートをしてみませんか?