Лексический и динамический интерпретатор на языке схем

Я до сих пор не понимаю, чем динамический интерпретатор отличается от лексического.

Я работаю над схемой, и мне очень трудно понять, как такой простой код, как этот, работает динамически и лексически.

      (define mystery
  (let ((x 2018))
    (lambda (y)
      (let ((result (cons x y)))
         (set! x (+ x 1))
         result))))

какие-нибудь указания?

3 ответа

Лексические привязки имеют ограниченную видимость и неограниченный срок жизни. Все функции «запоминают» среду, в которой они были созданы - такой вид функций называется лексическим замыканием .

В вашем примере эта часть:

      (let ((x 2018))
    (lambda (y) (let ((result (cons x y)))
                  (set! x (+ x 1)) result))))

возвращает функцию, которая запоминает среду с x = 2018. Эта функция привязана к символу, и когда вы ее вызываете, она изменяет значение x в этой среде.

      > (mystery 1)
'(2018 . 1)
> (mystery 1)
'(2019 . 1)

В Scheme с динамическими привязками (неограниченная видимость, ограниченный срок жизни) функции не запоминают среду, в которой они были созданы. Итак, функция mystery не запомнит среду с x = 2018 и вызовет (mystery 1) заканчивается ошибкой во время оценки (cons x y), потому что символ x не имеет значения.

Давайте просто создадим программу с вашим кодом:

      ;; a global binding
(define x 100)

;; your function
(define mystery
  (let ((x 2018))
    (lambda (y)
      (let ((result (cons x y)))
         (set! x (+ x 1))
         result))))

;; just to add newlines in prints
(define displayln
  (lambda (v) 
    (display v)
    (newline)))

;; a indirect call
(define local-test 
  (lambda (x)
    (displayln x)
    (displayln (mystery 'local))
    (displayln (mystery 'local))
    (displayln x)))

(define global-test
  (lambda ()
    (displayln x)
    (displayln (mystery 'global))
    (displayln (mystery 'global))
    (displayln x)))

;; program
(local-test 1)
(local-test 11)
(global-test 1)
(global-test 11)

Результаты обычной схемы зависят только от замыканий, а не от переменных, связанных со стеком вызовов:

      1
(2018 local)
(2019 local)
1
11
(2020 local)
(2021 local)
11
1
(2022 global)
(2023 global)
1
11
(2024 global)
(2025 global)
11

Результаты динамической «схемы» остаются загадкой как мертвый код. Он ничего не делает, поскольку привязки не сохраняются с объектом функции. Таким образом, только переменные в активном let и звонки совпадают:

      1
(1 local)
(2 local)
3
11
(11 local)
(12 local)
13
100
(100 global)
(101 global)
102
102
(102 global)
(103 global)
104
      (define mystery
  (let ((x 2018))
    (lambda (y)
      (let ((result (cons x y)))
         (set! x (+ x 1))
         result))))

Это не очень хороший пример для понимания разницы между динамической и статической привязкой. Это просто угловой случай.

Идея состоит в том, что при статическом связывании свободные переменные связаны со статической областью видимости (лексический код, который виден при записи), а при динамическом связывании они связаны с динамическим кодом (который хранится в стеке выполнения).

Ваш код оценивается как это лямбда-выражение:

      (lambda (y)
  (let ((result (cons x y)))
    (set! x (+ x 1))
    result))

В этом результате единственная свободная переменная - это X.

Каково значение X, когда вы применяете результат к значению для Y?

В статической области видимости будет 2018 год, при динамической привязке значение X будет храниться в стеке - например,

      (define X 100)
(define F (result 200)))

будет применяться result с привязкой X=100(X будут храниться в стеке). Конечно, значение X физически не хранится в стеке, а просто указатель на фрейм среды, в котором оно находится, или, возможно, в ячейке значения, если в среде выполняется повторное рутирование, и т. Д.

Чтобы понять ваше недоразумение, вы можете пройти курс лямбда-исчисления. И, конечно же, то, что я сказал здесь, предполагает, что вы используете общую интерпретацию, многие другие интерпретации могут быть связаны с тем же синтаксисом, что и ваш пример ввода, и т. Д.

Другие вопросы по тегам