7

有问题的性别是

(((lambda (b)
  (lambda (a)
    (+ b a))) 3) 5)

对我来说,它看起来应该评估为8,并且在其他lisp(例如Racket)中它确实如此,但是在elisp中它会抛出这个错误:

Debugger entered--Lisp error: (invalid-function ((lambda (b) (lambda (a) (+ b a))) 3))

它似乎在告诉我

((lambda (b)
  (lambda (a)
    (+ b a))) 3)

不是一个有效的函数。这似乎是错误的,因为当我评估该表达式时,我得到

(lambda (a) (+ b a))

这对我来说看起来像是一个有效的功能。有谁知道为什么会这样?它与动态范围有关吗?

4

4 回答 4

14

这里有两个问题。第一个是语法问题,正如其他答案所指出的那样。问题中提到的第二个问题是范围问题。

句法问题

在 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) ...), 代替。

于 2013-06-14T12:13:36.117 回答
3

简短的回答:这是Lisp-1 与 Lisp-2的效果

稍微长一点的答案:对于应用程序,Emacs Lisp 调用列表中第一个符号的函数值,但lambda只返回一个简单的函数对象。这意味着您必须使用funcall才能使事情正常进行。 funcall将其第二个参数应用于其余参数(很像apply,但后者将其最后一个参数解释为一个列表)。

(funcall (lambda (a) (funcall (lambda (b) (+ a b)) 5)) 3)
=> 8

查看 emacs 文档(例如C-h f funcall)以获取更多信息。

于 2013-06-14T08:15:43.633 回答
2

carof(((lambda (b) (lambda (a) (+ b a))) 3) 5)既不是具有关联的有效函数定义的符号,也不是符号,lambda因此它不是有效的函数调用。

于 2013-06-14T06:24:18.910 回答
1

FWIW,另一个重新排列会给你这个:

((lambda (b)
   ((lambda (a)
      (+ b a))
    3))
 5)

=> 8

(但我认为这不是你想要做的)

于 2013-06-14T12:25:38.567 回答