Объяснять различное поведение переменных, на которые ссылаются в продолжениях?
Ниже приведены два случая, которые по-разному относятся к значению i
через вызовы к сохраненным продолжениям. Чем можно объяснить разницу?
Случай А
>(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
Дело Б
> (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 ответ
Я делаю предположения здесь, так что прости меня, если я ошибаюсь:)
Я смог воспроизвести это в Racket и в моей любимой реализации Схемы. Проблема может скрываться в том, как продолжения реализованы в конкретной реализации Схемы. Итак, вот мои предположения:
- Реализация оценивает аргументы процедуры слева направо.
- Полученные значения хранятся в стеке.
- Стек копируется при вызове
call/cc
и копия содержит аргументы оцененной процедуры. - Когда продолжение вызвано, скопированный стек восстанавливается вместе с аргументами оцененной процедуры.
Это означает, что в
(set! i (+ i (call/cc (lambda (k) (set! cc k) 1))))
i
в +
заявка оценивается раньше call/cc
и его ценность 100
хранится в стеке. когда cc
называется это не переоценка i
и вместо этого использует значение 100
, оцененный ранее. Так что при звонке (cc 50)
ты получаешь
(set! i (+ 100 50))
Если вы поменяете местами call/cc
а также i
, затем i
будет переоценен и, таким образом, вы получите правильные результаты.
(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