【问题标题】:format of lambda in emacs lispemacs lisp 中的 lambda 格式
【发布时间】:2012-01-26 16:36:58
【问题描述】:

我正在尝试在 emacs lisp 中应用闭包。我在这里找到了一个帖子: How do I do closures in Emacs Lisp?

使用如下代码:

(defun foo (x) `(lambda () ,x)) (message (string (funcall (foo 66)))))

但是遵循 emacs documentation lambda 的格式应该像 '(lambda () x) ==> 使用这种格式,我得到一个错误:符号的值作为变量是无效的:x

当“,”在“()”和“x”之间添加时,一切正常。

为什么?

【问题讨论】:

  • Emacs Lisp 没有这样的闭包。另请阅读 FUNCALL 如何使用它的文档。
  • 在 Emacs 24 中,只要为缓冲区/文件设置了 lexical-binding,您的代码就可以工作。

标签: emacs lisp eval quote backquote


【解决方案1】:

这个答案提供了@Daimrod 正确答案第一部分背后的一些细节。

您的问题是为什么这有效:

(defun foo (x) `(lambda () ,x)) (message (string (funcall (foo 66))))

这不起作用:

(defun foo (x) '(lambda () x)) (message (string (funcall (foo 66))))

首先,第二个中的引号 (') 是不必要的,因为在 Emacs Lisp 中,lambda 形式是自我评估的。也就是说,'(lambda (...) ...) 的作用一般与(lambda (...) ...) 相同。

但是,反引号 (`) 而不是引号 ('),做什么

在反引号表达式中,逗号 (,) 表示将下一个表达式替换为其值,即求值。所以这个:

`(lambda () ,x)

表示创建并返回一个列表,其第一个元素是符号lambda(未计算),第二个元素是()(未计算),第三个元素是 变量x。相当于评估这段代码,使用函数list

(list 'lambda '() x)

这就是你想要的:替换 x 为其当前值(在这种情况下,它在函数foo 中的值,即foo 的参数的值) .

但是这个:

'(lambda () x)

(同样,因为 lambda 形式是自评估的,(lambda () x))返回此列表:(lambda () x)。当使用动态范围(Emacs Lisp 中的默认范围机制)进行评估时,x 是未绑定的——它没有任何价值。因此会引发 void-variable 错误。

【讨论】:

    【解决方案2】:

    这是因为 Emacs Lisp 是动态作用域的,因此 foo 返回一个 lambda,其中 x 是空闲的。这就是错误告诉你的内容。

    要在 Emacs Lisp 中进行闭包,您必须使用 lexical-let,它模拟词法绑定,因此允许您进行真正的闭包。

    (defun foo (x)
      (lexical-let ((x x))
                   (lambda () x)))
    
    (message (string (funcall (foo 66))))
    

    这里有一些来自 Emacs Wiki 的链接:

    1. Dynamic Binding Vs Lexical Binding
    2. Fake Closures

    请注意,您可以使用这样的 let 定义 x

    (defun foo (x)
      (lambda () x))
    
    (message (string (let ((x 66)) (funcall 
                                    (foo 'i-dont-care)))))
    

    【讨论】:

    • 实际上,taht 代码甚至更 hackier,它相当于:(defun foo (x) (list 'lambda nil x)) 所以(foo 66) 返回(lambda () 66),我敢肯定这里有各种不经意的捕获可能......
    • @Vatine: 是的,我知道反引号是什么,但你必须明白引号是什么=> 它阻止了对表单的评估,所以(defun foo (x) '(lambda () x))(foo 66) 返回(lambda () x)然后您尝试对此进行调用,但解释器不知道它是什么。与允许使用逗号进行评估的反引号相反。
    • 但是在原帖中有一个反引号,所以它返回(lambda () 66),而不是(lambda () x)
    • @Vatine:是的,加上反引号它可以工作。如果文档说 lambda 应该用引号格式化,那么它是为了像这样的高阶 (mapcar '(lambda (el) (+ el 1)) '(1 2 3 4)), but in your case you want to evaluate x` 来构建你的闭包。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多