这里有两个问题。第一个是语法问题,正如其他答案所指出的那样。问题中提到的第二个问题是范围问题。
句法问题
在 Emacs Lisp(以及Lisp-2系列中的其他 Lisp)中,函数调用看起来像(f args...)
wheref
是具有函数值的符号或lambda
表达式。例如,
(list 1 2 3)
=> (1 2 3)
因为list
有一个函数绑定。还,
((lambda (x y) (list x x y y)) 1 2)
=> (1 1 2 2)
因为(lambda (x y) (list x x y y))
是一种lambda
表达方式。但是,您不能做的是使用 which of 的值是函数的东西。
(let ((id (lambda (x) x)))
(id 3))
Lisp error: (void-function id)
发出信号 但是我们可以使用以下方法调用函数值funcall
:
(let ((id (lambda (x) x)))
(funcall id 3))
=> 3
注意:这是一种相当好的看待它的方式,但实际上事情要复杂一些。有关详细信息和函数间接等深奥的内容,请参见手册中的9.2 形式。
所以,现在我们可以解决语法问题。重新格式化以指示哪些函数正在获取哪些参数的原始代码是:
(((lambda (b)
(lambda (a)
(+ b a)))
3)
5)
据我了解,目的是首先(lambda (b) ...)
使用参数调用3
以取回匿名函数(lambda (a) ...)
. 在 Emacs Lisp 中,这将是:
((lambda (b)
(lambda (a)
(+ b a)))
3)
=> (lambda (a) (+ b a))
现在,您还想使用 调用返回的匿名函数5
。我们曾经funcall
这样做:
(funcall ((lambda (b)
(lambda (a)
(+ b a)))
3)
5)
范围问题
令人失望的是,这段代码生成了一个Lisp error: (void-variable b)
. 这就是我们最终遇到动态与词法作用域问题的地方。因为变量b
是动态绑定的,所以它的值不会保存在匿名函数(lambda (a) (+ b a))
中。我们可以通过将整个表单包围在绑定的东西中b
并查看发生了什么来检查是否发生了这种情况:
(let ((b 100))
(funcall ((lambda (b)
(lambda (a)
(+ b a)))
3)
5))
=> 105
我不是 Emacs Lisp 黑客,所以我不确定在 Emacs 中获得词法闭包的最佳方法。我读到 Emacs 24 有它,但我仍然在这里 23。但是,基于这个答案,我们可以使用lexical-let
来获得我们需要的结果:
(funcall ((lambda (b)
(lexical-let ((b b))
(lambda (a)
(+ b a))))
3)
5)
=> 8
lexical-let
建立我们需要的词法绑定,以便匿名函数(lambda (a) ...)
确实将其3
嵌入其中。更具体地说,我们引入了 的词法绑定b
,而引用的正是该词法绑定(lambda (a) …)
。事实上,如果我们现在查看返回的匿名函数,它不是简单的(lambda (a) (+ b a))
,而是以更复杂(也不太有用)的方式打印出来的:
((lambda (b)
(lexical-let ((b b))
(lambda (a)
(+ b a))))
3)
=> (lambda (&rest --cl-rest--) (apply (lambda (G27322 a) (+ ... a)) (quote --b--) --cl-rest--))
b
顺便说一句,词法绑定变量与动态绑定的名称相同并不重要b
;我们可以使用(lexical-let ((c b)) ... (+ c a) ...)
, 代替。