使用函数中的lambda值作为列表的第一个元素

我正在阅读Peter Norvig的人工智能编程范例,我遇到了一个我自己无法解决的问题(这是我对Lisp的介绍).真的,这个问题相当小,但显然不是我的小脑能解决的问题.

为什么当函数的值是lambda时,将该函数用作列表的第一个元素是错误的.例如:

Lisp的:

(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有两个名称空间 – 一个用于函数,另一个用于值.为了实现这一点,对于函数应用程序的头部位置有两种不同的评估规则,其余的 – 第一种将符号评估为函数名称,第二种将符号评估为变量引用.显然,值实际上是一个函数 – 例如,如果你编写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变量而不是函数.对于这些情况,有一种称为函数的特殊形式,它将其内容作为函数进行计算并将其作为值返回:

(my-mapcar (function 1+) '(1 2 3))

它可以缩写为#’:

(my-mapcar #'1+ '(1 2 3))

这不是故事的结尾 – 举几个例子:

>在某些情况下,一个简单的引用名称可以作为一个函数 – 例如最后一个例子中的’1工作 – 但这是一种只能看到全局绑定名称的hack,所以#’几乎总是更好
>一个类似的hack可以和lambda一起使用 – 所以你可以使用(my-mapcar'(lambda(x)(1 x))'(1 2 3)),实际上你可以使用(list’lambda'( x)'(1 x))甚至更糟(和IIRC,非便携式),但使用(lambda(x)(1 x))是有效的,因为它隐含地包含在#’中(尝试将lambda形式扩展为宏,你会看到它).一个相关的hack使得使用lambda表达式作为函数应用程序的头部(这是你尝试过的事情之一)很好.
>让etc等绑定本地值,在某些情况下你会想要绑定局部函数 – 为此,有新的绑定结构:flet和labels

如果所有这些看起来都很奇怪和/或过于复杂,那么你并不孤单.这是Common Lisp和Scheme之间的主要区别之一. (差异导致两种语言中常见习语的变化:方案代码往往比Common Lisp代码更频繁地使用更高阶函数.像往常一样,这些宗教问题,有些人主张CL做什么,声称高阶函数令人困惑,所以他们喜欢明确的代码内提醒.)

相关文章
相关标签/搜索