我正在尝试动态创建一个返回一个常量值的函数。
在 JavaScript 和其他现代命令式语言中,我会使用闭包:
function id(a) {
return function() {return a;};
}
但是 Emacs lisp 不支持这些。
我可以创建身份功能和部分功能应用程序的组合,但也不支持。
那么我该怎么做呢?
用 lexical-let 找到了另一个解决方案
(defun foo (n)
(lexical-let ((n n)) #'(lambda() n)))
(funcall (foo 10)) ;; => 10
Emacs 24 中的真实(非假)闭包。
尽管 Emacs 24 在变量lexical-binding的值为t时具有词法提取功能,但defunc特殊形式在词法绑定的上下文中无法正常工作(至少在 Emacs 24.2.1 中没有。)这使得难以但并非不可能定义真正的(不是假的)闭包。例如:
(let ((counter 0))
(defun counting ()
(setq counter (1+ counter))))
将无法按预期工作,因为defun中的符号计数器将绑定到该名称的全局变量(如果有的话),而不是let中的词法变量定义。当调用函数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))))
它将按预期工作并在每次调用时更新词法绑定变量计数,同时返回新值。
警告:如果您尝试解解**与词法绑定变量之一同名的函数,该宏将无法正常工作。即,如果您执行以下操作:
(let ((dont-do-this 10))
(defun** dont-do-this ()
.........
.........))
我无法想象有人真的这样做,但值得一提。
注意:我已将宏命名为defun** ,以便它不会与cl包中的宏defun *发生冲突,但它不以任何方式依赖于该包。
愚蠢的想法:怎么样:
(defun foo (x)
`(lambda () ,x))
(funcall (foo 10)) ;; => 10
Emacs lisp 只有动态作用域。有一个lexical-let
宏通过一个相当可怕的 hack 来近似词法范围。
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 ...