thunk는 일을 미루고 값을 기억한다.
태그가 붙은 가변 객체로 계산을 보류한 뒤 첫 force 결과로 표현을 바꿀 수 있습니다.
call-by-need를 평가기 안에서 보이게 하는 표현 변화는 무엇일까요?
- Represent delayed work as a tagged mutable vector
- Distinguish an unevaluated thunk from an evaluated thunk
- Force a suspended computation only when its value is demanded
- Memoize the first value for later demands
make-thunk stores a thunk tag, a zero-argument computation, and an empty result slot. Creating that vector does not call the computation, so even a body that would fail can remain harmless when nobody demands it.
force-it inspects the tag. On the first demand it calls the stored computation, changes the tag to evaluated-thunk, discards the computation, and stores the value. Later demands select the cached slot and perform no computation again. This exposes the representation change that a lazy evaluator normally hides behind argument handling.
(begin
(define (make-thunk computation)
(vector 'thunk computation #f))
(define (force-it object)
(if (eq? (vector-ref object 0) 'evaluated-thunk)
(vector-ref object 2)
(let ((value ((vector-ref object 1))))
(vector-set! object 0 'evaluated-thunk)
(vector-set! object 1 #f)
(vector-set! object 2 value)
value)))
(define calls 0)
(define delayed
(make-thunk
(lambda ()
(set! calls (+ calls 1))
(* calls 10))))
(list (force-it delayed)
(force-it delayed)
calls
(vector-ref delayed 0)))- 출력
- —
- 값
- —
- 진단
- —
The first program returns (10 10 1 evaluated-thunk). The second returns (thunk 0) without evaluating the division by zero.
In the first run, find one application of the stored computation and the vector mutations that replace the thunk representation. The second force reads the cached value. In the second run, confirm that no division application or calls assignment occurs.
힌트를 보기 전에 프로그램을 바꿔 보세요.
Force delayed a third time in the first program. Predict the three values, calls count, and final tag before running it.
힌트 하나 보기
Once the tag is evaluated-thunk, force-it reads slot 2 without calling slot 1.