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

标签 lambda lisp common-lisp lisp-2

我正在阅读 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/

相关文章:

c++ - 为什么不能将lambda用作非类型模板参数?

clojure - 我如何用这个 Clojure 代码重写 (def)?

scheme - Nil 不是空列表吗?

lisp - 具有更高级别功能的 lisp 中的二进制搜索

c++ - C++11 中的高阶函数

java - Optional.ofNullable() 是否应该用于空检查?

emacs - 常见的 lisp 和 emacs

time-complexity - Common Lisp 中各种序列类型的 ELT 函数的时间复杂度

c++ - 从 lambda 构造 std::function 参数

lisp - 实习生制作的符号