Реальные (не поддельные) замыкания в Emacs 24.
Хотя Emacs 24 имеет лексическую черпаемость, когда переменная лексическая привязка имеет значение t , специальная форма defun не работает должным образом в контекстах с лексической привязкой (по крайней мере, в Emacs 24.2.1.) Это затрудняет, но не невозможно определить реальные (не поддельные) замыкания. Например:
(let ((counter 0))
(defun counting ()
(setq counter (1+ counter))))
не будет работать должным образом, потому что символ counter в defun будет связан с глобальной переменной этого имени, если она есть, а не с лексической переменной, определенной в пусть . Когда вызывается функция counting , если глобальная переменная не существует, то она, очевидно, потерпит неудачу. Однако, если есть такая глобальная переменная, она будет обновлена, что, вероятно, не то, что предполагалось, и может быть трудно отследить ошибку, так как может показаться, что функция работает правильно.
Байтовый компилятор выдает предупреждение, если вы используете defun таким образом, и, вероятно, проблема будет решена в какой-то будущей версии Emacs, но до тех пор может использоваться следующий макрос:
(defmacro defun** (name args &rest body)
"Define NAME as a function in a lexically bound context.
Like normal `defun', except that it works correctly in lexically
bound contexts.
\(fn NAME ARGLIST [DOCSTRING] BODY...)"
(let ((bound-as-var (boundp `,name)))
(when (fboundp `,name)
(message "Redefining function/macro: %s" `,name))
(append
`(progn
(defvar ,name nil)
(fset (quote ,name) (lambda (,@args) ,@body)))
(if bound-as-var
'nil
`((makunbound `,name))))))
Если вы определите , считая следующим образом:
(let ((counter 0))
(defun** counting ()
(setq counter (1+ counter))))
будет работать как положено и обновлять лексически связанную переменную count каждый раз, когда она вызывается, и возвращать новое значение.
CAVEAT: Макрос не будет работать должным образом, если вы попытаетесь defun ** функции с тем же именем, что и у одной из лексически связанных переменных. Т.е. если вы делаете что-то вроде:
(let ((dont-do-this 10))
(defun** dont-do-this ()
.........
.........))
Я не могу представить, чтобы кто-то на самом деле делал это, но это стоило упомянуть.
Примечание: я назвал макрос defun ** , чтобы он не конфликтовал с макросом defun * в cl , но от этого пакета это никак не зависит.