Понимание выражения "пусть" в Лиспе


Я чрезвычайно новичок в lisp, имел предыдущий опыт работы с функциональным программированием (Haskell, SML). Почему этот код возвращает 14, а не 10 (т. е. 1 + 2y + 3 + 1)?

(defvar x 1)

(defun g (z)
  (+ x z))

(defun f (y)
  (+ (g 1)
     (let ((x (+ y 3)))
        (g (+ y x)))))

(f 2)
2 5

2 ответа:

Потому что вы использовали (DEFVAR X 1), который объявляет X глобальной специальной переменной. Это затем заставляет каждую последующую привязку X использовать динамическую привязку: здесь в (LET ((X ....

Стиль и условности в Lisp

Соглашение в Lisp: используйте *X* вместо X для специальных переменных.

(defvar *x* 1)

Тогда ваш код:

(defvar *x* 1)   ; global special variable *X*

(defun g (z)
  (+ *x* z))     ; use special variable *X*

(defun f (y)
  (+ (g 1)
     (let ((x (+ y 3)))    ; lexical binding of X
        (g (+ y x)))))     ; use lexical binding of X

Запуск:

? (f 2)
10

Причина в том, что вы используете диалект Lisp с динамической привязкой (ссылка на хорошее описание этого из документации Emacs Lisp).

В деталях ваша программа ведет себя так, потому что новая привязка для x, созданная выражением let, занимает место (defvar x 1), Когда g вызывается из выражения let. Таким образом, вместо добавления 1 к своему аргументу функция g добавляет текущее значение x, которое равно 5, когда внутри let выражение.