我正在尝试创建一个可以返回一个常量值的函数。
在JavaScript和其他现代命令式语言中,我会使用闭包:
function id(a) {
return function() {return a;};
}
但是Emacs lisp不支持这些。
我可以创建身份功能和部分功能应用程序的混合,但它也不受支持。
那我该怎么做?
答案 0 :(得分:29)
使用lexical-let
找到另一个解决方案(defun foo (n)
(lexical-let ((n n)) #'(lambda() n)))
(funcall (foo 10)) ;; => 10
答案 1 :(得分:14)
Emacs中的真实(非假)闭包。
虽然当变量词汇绑定具有值 t 时,Emacs 24具有词法舀取,但 defun 特殊表单在词汇绑定的上下文(至少在Emacs 24.2.1中没有。)这使得很难,但不不可能,定义真正的(非假的)闭包。例如:
(let ((counter 0))
(defun counting ()
(setq counter (1+ counter))))
将无法按预期工作,因为 defun 中的符号计数器将绑定到该名称的全局变量(如果有),而不是词法变量在让中定义。当调用函数计数时,如果全局变量不存在,那么它肯定会失败。然而,如果有这样一个全局变量,它会被更新,这可能不是预期的,并且可能是难以追踪的错误,因为该函数可能看起来工作正常。
如果您以这种方式使用 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 * < / strong>在 cl 包中,但它不以任何方式依赖于该包。
答案 2 :(得分:8)
愚蠢的想法:如何:
(defun foo (x)
`(lambda () ,x))
(funcall (foo 10)) ;; => 10
答案 3 :(得分:7)
Emacs lisp只有动态范围。有一个lexical-let
宏通过一个相当可怕的黑客来近似词汇范围。
答案 4 :(得分:6)
我对Emacs Lisp并不坚定,但就我所知,与Common Lisp的一个重大区别在于它始终使用动态范围。 Emacs Lisp Manual表示Emacs Lisp没有闭包。
我会尝试应用我对动态范围的理论知识。
如果您的函数id
只返回my-id
的值:
(defun id () my-id)
你可以在其他功能中使用它:
(defun some-other-place () (id))
以及在通过id
调用途中的某个地方{绑定my-id
一个让:
(defun even-elsewhere () (let ((my-id 5)) (some-other-place)))
这应该回馈5。
我知道当你习惯词法范围时,动态范围是一种奇怪的野兽,但也许你可以用它来实现你想要的行为。
答案 5 :(得分:4)
Emacs 24具有词汇绑定。
答案 6 :(得分:0)
答案 7 :(得分:0)
;; -*- 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 ...