我正在阅读 Peter Norvig 的人工智能编程范式,我遇到了一个我自己无法解决的问题(这是我对 Lisp 的介绍)。这个问题确实很小,但显然不是我的小脑袋能解决的。
为什么当函数的值为 lambda 时,将该函数用作列表的第一个元素是错误的。例如:
口齿不清:
(defun some-func ()
#'(lambda (x) x))
;; At REPL
;; Does not work
> ((some-func) 1)
;; Does work
> ((lambda (x) x) 1)
;; Also works
> (funcall (some-func) 1)
我希望这是有道理的!
最佳答案
这是一个很好的问题,而且 Common Lisp 可能非常令人困惑。问题是,由于历史原因,Common Lisp 有两个 namespace ——一个用于函数,另一个用于值。为实现这一点,函数应用程序的头部位置和其余位置有两种不同的求值规则——第一种将符号作为函数名求值,第二种将符号作为变量引用求值。显然,在某些情况下,值实际上是一个函数——例如,如果您编写一个 mapcar
函数,您会想要做类似的事情
(defun my-mapcar (f l)
(if (null l)
'()
(cons (f (car l)) (my-mapcar f (cdr l)))))
但这行不通——它会提示 f
是一个未知函数。对于这些情况,有一个名为 funcall
的特殊函数,它接收一个函数和该函数的参数,并将像往常一样应用该函数——因为 funcall
是一个普通的函数,它的参数都像往常一样被评估(作为值)。所以上面的问题应该通过使用它来解决:
(defun my-mapcar (f l)
(if (null l)
'()
(cons (funcall f (car l)) (my-mapcar f (cdr l)))))
正如您现在可能怀疑的那样,存在镜像情况——您希望将某物作为函数求值,不作为值求值。例如,这不起作用:
(my-mapcar 1+ '(1 2 3))
因为它指的是 1+
变量而不是函数。对于这些情况,有一种称为 function
的特殊形式,它将其内容作为函数求值并将其作为值返回:
(my-mapcar (function 1+) '(1 2 3))
可以用#'
简写:
(my-mapcar #'1+ '(1 2 3))
故事还没有结束——举几个例子:
在某些情况下,一个简单的引用名称可以用作一个函数——例如上一个示例中的
'1+
可以工作——但这是一种可以看到的 hack只有全局绑定(bind)的名称,所以#'
几乎总是更好lambda
可以使用类似的 hack -- 所以你可以使用(my-mapcar '(lambda (x) (1+ x)) '(1 2 3))
,事实上,你可以使用(list 'lambda '(x) '(1+ x))
这更糟(和 IIRC,不可移植),但是使用(lambda (x) (1+ x))
可以工作,因为它被隐式包装在#'
中(尝试将lambda
形式扩展为宏,你会看到它)。一个相关的 hack 可以很好地使用lambda
表达式作为函数应用程序的头部(这是您尝试过的事情之一)。let
等绑定(bind)局部值,在某些情况下,您会希望改为绑定(bind)局部函数——为此,有新的绑定(bind)结构:flet
和标签
如果所有这些看起来很奇怪和/或过于复杂,那么您并不孤单。这是 Common Lisp 和 Scheme 之间的主要区别之一。 (这种差异导致两种语言中常用习语的变化:Scheme 代码比 Common Lisp 代码更频繁地使用高阶函数。像往常一样,对于这类宗教问题,有些人赞成 CL 所做的事情,声称高阶函数令人困惑,因此他们喜欢显式的代码内提醒。)
关于lambda - 使用函数中的 lambda 值作为列表的第一个元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5850553/