Объяснять различное поведение переменных, на которые ссылаются в продолжениях?

Ниже приведены два случая, которые по-разному относятся к значению 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
Другие вопросы по тегам