Ответ 1
Глупая идея: как насчет:
(defun foo (x)
`(lambda () ,x))
(funcall (foo 10)) ;; => 10
Я пытаюсь создать функцию "на лету", которая вернет одно постоянное значение.
В JavaScript и других современных императивных языках я бы использовал закрытие:
function id(a) {
return function() {return a;};
}
но Emacs lisp не поддерживает их.
Я могу создать сочетание функции идентификации и приложения с частичной функцией, но она также не поддерживается.
Итак, как мне это сделать?
Глупая идея: как насчет:
(defun foo (x)
`(lambda () ,x))
(funcall (foo 10)) ;; => 10
Нашел другое решение с лексико-let
(defun foo (n)
(lexical-let ((n n)) #'(lambda() n)))
(funcall (foo 10)) ;; => 10
Реальные (не поддельные) затворы в Emacs 24.
Хотя Emacs 24 имеет лексическое scooping, когда переменная lexical-binding имеет значение t, специальная форма defun не работает должным образом в лексически связанной контексты (по крайней мере, не в Emacs 24.2.1.) Это затрудняет, но не невозможно, определение реальных (не поддельных) закрытий. Например:
(let ((counter 0))
(defun counting ()
(setq counter (1+ counter))))
не будет работать так, как ожидалось, потому что символ счетчик в defun будет привязан к глобальной переменной этого имени, если он есть, а не лексическая переменная определите в let. Когда вызывается функция подсчет, если глобальная переменная doesnt существует, она, очевидно, терпит неудачу. Если есть такая глобальная переменная, она обновляется, что, вероятно, не является тем, что было предназначено, и может быть трудно отследить ошибку, так как функция может работать правильно.
Байт-компилятор дает предупреждение, если вы используете 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, однако это никак не зависит от этого пакета.
Emacs lisp имеет только динамическое масштабирование. Там макрос lexical-let
, который аппроксимирует лексику, просматривая довольно страшный хак.
Я не уверен в Emacs Lisp, но, насколько я знаю, большая разница с Common Lisp заключается в том, что он использует динамическое масштабирование во всем. Emacs Lisp Manual утверждает, что Emacs Lisp не имеет замыканий.
Я попытаюсь применить свои теоретические знания о динамическом охвате.
Если у вас есть функция id
, которая возвращает значение my-id
:
(defun id () my-id)
и вы используете его в некоторой другой функции:
(defun some-other-place () (id))
и где-то на пути к вызову id
вы связываете my-id
через, например. a let:
(defun even-elsewhere () (let ((my-id 5)) (some-other-place)))
это должно вернуть 5.
Я знаю, что динамическое масштабирование - странный зверь, когда вы привыкли к лексическому охвату, но, возможно, вы можете использовать его для реализации желаемого поведения.
Emacs 24 имеет лексическую привязку.
;; -*- lexical-binding:t -*-
(defun create-counter ()
(let ((c 0))
(lambda ()
(setq c (+ c 1))
c)))
(setq counter (create-counter))
(funcall counter) ; => 1
(funcall counter) ; => 2
(funcall counter) ; => 3 ...