如何在Emacs Lisp中关闭?

时间:2009-02-27 03:14:17

标签: emacs closures elisp

我正在尝试创建一个可以返回一个常量值的函数。

在JavaScript和其他现代命令式语言中,我会使用闭包:

function id(a) {
    return function() {return a;};
}

但是Emacs lisp不支持这些。

我可以创建身份功能和部分功能应用程序的混合,但它也不受支持。

那我该怎么做?

8 个答案:

答案 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具有词汇绑定。

http://www.emacswiki.org/emacs/LexicalBinding

答案 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 ...