Не могу понять вывод во втором случае - PullRequest
1 голос
/ 01 апреля 2019
(define c
  (let ((d 10))
    (set! d (- d 2))
    d))
(define c1
  (let ((d 10))
   (lambda (p)
     (set! d (- d p))
     d)))

Теперь для c c вывод равен 8 8.

Но для (c1 2) (c1 2) вывод равен 8 6.Почему это так?

Я думаю, что мне нужно понять, как на самом деле оцениваются вызовы функций.

По моему мнению, оценка должна быть как (во втором случае) для первогоПри вызове создается локальная среда для функции c1, где значение d равно 10, а затем оценка процедуры происходит нормально.Затем, как только этот вызов заканчивается, вся среда разрушается, и для второго вызова происходит тот же самый процесс (как выше).Таким образом, второе выходное значение также должно быть 8. Но это 6, почему это так?

Ответы [ 2 ]

1 голос
/ 02 апреля 2019

Вы думаете об этом:

(define c1
  (let ((d 10))
    (lambda (p)
      (set! d (- d p))
      d)))

Это точно так же, как:

(define c1
  (lambda (p)
    (let ((d 10))
      (set! d (- d p))
      d)))

Это не так.В первом случае переменная d создается перед лямбда-выражением, и, таким образом, она является одной и той же свободной переменной для каждого вызова c1.Таким образом, изменение d изменяет следующий вызов.

Второй создает d при вызове и уничтожается после завершения вызова.

В первой схеме оценивается форма let.Он создает d, а затем оценивает лямбду, поэтому d становится свободной переменной в своем закрытии, которое возвращается.Затем синтаксис определения создает глобальную переменную c1 с этим полученным значением замыкания.let находится вне области видимости, но d не собирает мусор, поскольку на него все еще ссылается одно значение - замыкание.

0 голосов
/ 02 апреля 2019

A let можно переписать как немедленное применение lambda абстракции

(mylet ([var rhs] ...) body ...) => ((lambda (var ...) body ...) rhs ...)

Обезвоживание c let урожайность

(define c ((lambda (d) (set! d (- d 2)) d) 10))

Это просто приложение 10 к функции (которую мы называем f)

(define f (lambda (d) (set! d (- d 2)) d))
(define c 
  (f 10))
c
c

Что касается c1, у нас есть вложенные лямбды

(define f1 (lambda (d) (lambda (p) (set! d (- d p)) d)))
((f1 10) 2)
((f1 10) 2)

8 и 8. (Это то, что вы ожидали). Но на самом деле, то, что происходит,

(define c1
  (f1 10))
(c1 2)
(c1 2)

возвращает 8 и 6

d получает memoised ( здесь - это пример Фибоначчи, использующего ту же процедуру запоминания и set!).

Более того, для set! вы не можете иметь наивную замену. Оценочная модель ракетки объясняет, как «создается новое местоположение для каждой переменной в каждом приложении»:

Поскольку значение, связанное с аргументной переменной x, можно изменить, значение не может быть заменено на x, когда процедура начинается приложенное.

tl; dr оценка c1 дает лямбду, которая закрывается в ожидании замены (окружение). Который затем мутирует на set! при каждом вызове.

...