Как мне делать замыкания в Emacs Lisp?
Я пытаюсь на лету создать функцию, которая возвращала бы одно постоянное значение.
В JavaScript и других современных императивных языках я бы использовал замыкания:
function id(a) {
return function() {return a;};
}
но Emacs lisp их не поддерживает.
Я могу создать сочетание функции идентификации и приложения частичной функции, но это тоже не поддерживается.
Так как мне это сделать?
Ответов (7)7
Настоящие (не поддельные) замыкания в Emacs 24.
Хотя в Emacs 24 есть лексическое копирование, когда переменная lexical-binding имеет значение t , специальная форма defun не работает должным образом в лексически связанных контекстах (по крайней мере, не в Emacs 24.2.1). Это затрудняет, но не делает невозможным, определить настоящие (не поддельные) замыкания. Например:
(let ((counter 0))
(defun counting ()
(setq counter (1+ counter))))
не будет работать должным образом, потому что счетчик символов в defun будет привязан к глобальной переменной этого имени, если она есть, а не к лексической переменной, определенной в let . Когда вызывается функция подсчета , если глобальная переменная не существует, она, очевидно, завершится ошибкой. Однако, если существует такая глобальная переменная, она должна быть обновлена, что, вероятно, не является тем, что было задумано, и может быть трудно отследить ошибку, поскольку функция может работать должным образом.
Компилятор байтов выдает предупреждение, если вы используете 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))))
он будет работать, как ожидалось, и обновлять лексически привязанный счетчик переменных каждый раз, когда он вызывается, при этом возвращая новое значение.
ПРЕДОСТЕРЕЖЕНИЕ: макрос не будет работать должным образом, если вы попытаетесь отключить ** функцию с тем же именем, что и одна из лексически связанных переменных. Т.е. если вы сделаете что-то вроде:
(let ((dont-do-this 10))
(defun** dont-do-this ()
.........
.........))
Я не могу представить, чтобы кто-то на самом деле делал это, но об этом стоило упомянуть.
Примечание. Я назвал макрос defun **, чтобы он не конфликтовал с макросом defun * в пакете cl , однако он никоим образом не зависит от этого пакета.