问题
Following are two case which have different behaviors regarding the value of i
across calls to stored continuations. How can the difference be explained?
Case A
>(define cc #f)
>(define (x)
(let ((i 0))
(set! i (+ i 100))
(+ i (+ i (call/cc (lambda (k) (set! cc k) 1)))) ; call/cc is not included by set!
(set! i (+ i 10))
i))
> (x)
110
> (cc 50) ; the context variable i changes as cc calling
120
> (cc 50)
130
Case B
> (define cc #f)
> (define (x)
(let ((i 0))
(set! i (+ i 100))
(set! i (+ i (call/cc (lambda (k) (set! cc k) 1)))) ; call/cc is included by set!
(set! i (+ i 10))
i))
> (x)
111
> (cc 50) ; the context variable i always be 0, not changing as cc calling
160
> (cc 50)
160
回答1:
I'm making assumptions here, so forgive me if I'm wrong :)
I was able to reproduce this in Racket and in my pet Scheme implementation. The problem may hide in the way the continuations are implemented in the specific Scheme implementation. So here are my assumptions:
- The implementation evaluates the arguments of a procedure from left to right.
- The resulting values are stored in a stack.
- The stack is copied when calling
call/cc
and the copy contains the evaluated procedure arguments. - When the continuation is called the copied stack is restored together with the evaluated procedure arguments.
This means that in
(set! i (+ i (call/cc (lambda (k) (set! cc k) 1))))
i
in the +
application is evaluated before call/cc
and its value 100
is stored in the stack. When cc
is called it does not reevaluate i
and instead uses the value 100
, evaluated before. So when calling (cc 50)
you get
(set! i (+ 100 50))
If you switch the places of call/cc
and i
, then i
will be reevaluated and thus you get correct results.
(define cc #f)
(define (x)
(let ((i 0))
(set! i (+ i 100))
(set! i (+ (call/cc (lambda (k) (set! cc k) 1)) i))
(set! i (+ i 10))
i))
> (x)
111
> (cc 50)
171
> (cc 50)
231
来源:https://stackoverflow.com/questions/26997717/explaining-different-behavior-of-variables-referenced-in-continuations