我试图创建一个即时生成的函数,它将返回一个常量值。
在JavaScript和其他现代命令式语言中,我会使用闭包:
function id(a) {
return function() {return a;};
}
但是Emacs lisp不支持这些。
我可以创建一个标识函数和部分函数应用的混合体,但也不受支持。
那么我该怎么做呢?
我试图创建一个即时生成的函数,它将返回一个常量值。
在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时具有词法作用域,但是在词法绑定的上下文中,defun特殊形式不能正常工作(至少在 Emacs 24.2.1 中不行)。这使得定义真正(非伪装)闭包变得困难,但并非不可能。例如:
(let ((counter 0))
(defun counting ()
(setq counter (1+ counter))))
由于在defun中的符号counter将绑定到该名称的全局变量(如果存在),而不是在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))))
每次调用时,它将按预期工作并更新词法绑定变量 count ,同时返回新值。
注意:如果您尝试使用与词法绑定变量之一同名的函数进行 defun ** ,则宏将无法正常工作。 也就是说,如果您执行以下操作:
(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
宏,通过一种相当可怕的技巧近似词法作用域。
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 ...