30

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

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

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

但是 Emacs lisp 不支持这些。

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

那么我该怎么做呢?

4

7 回答 7

33

用 lexical-let 找到了另一个解决方案

(defun foo (n) 
    (lexical-let ((n n)) #'(lambda() n)))

(funcall (foo 10)) ;; => 10
于 2009-02-27T04:06:12.513 回答
14

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 *发生冲突,但它不以任何方式依赖于该包。

于 2012-12-30T03:38:56.640 回答
9

愚蠢的想法:怎么样:

(defun foo (x)
  `(lambda () ,x))

(funcall (foo 10))  ;; => 10
于 2009-02-27T03:47:19.470 回答
7

Emacs lisp 只有动态作用域。有一个lexical-let宏通过一个相当可怕的 hack 来近似词法范围。

于 2009-02-27T05:14:10.243 回答
3

Emacs 24 具有词法绑定。

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

于 2012-11-04T03:11:09.187 回答
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 ...
于 2019-05-11T07:46:14.287 回答
-1

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

于 2011-09-05T05:43:04.763 回答