为什么要使用Sharp引用Lambda表达式?

22

这是一种在通用Lisp中经常使用的技术,它在《On Lisp》一书中有详细介绍:

> (mapcar #'(lambda (x) (+ x 10))
         '(1 2 3))
(11 12 13)
为什么需要sharp-quote,甚至可能?lambda表达式返回函数对象,而sharp引用从名称中返回函数对象。我还听到过关于lambda表达式是否是名称的矛盾信息 - 特别是On Lisp与标准相矛盾,但他的代码似乎也与标准相矛盾。

在elisp中,似乎根本不需要sharp-quote。令人惊讶的是,这其中没有讨论词法作用域,因此不清楚为什么它应该与Common Lisp有所不同。但是,这个答案说未引用的lambda表达式根本没有意义,因此,具有讽刺意味的是,可以将未引用的lambda表达式引入作为引用的语法糖。但这与elisp手册的说法相矛盾,即“在Emacs Lisp中,这样的列表是一个有效的表达式,它将计算为函数对象。”

这个问题被同时标记为common和e-lisp,因为我试图理解两者背后的理论模型,并通过了解它们之间的差异来学习它们。 (更实际地说,我正在尝试学习elisp,但发现了很多关于Common Lisp的好资源,因此正在同时学习两者)。


请参阅Elisp参考手册,其中暗示了function引用有助于字节编译器识别应该编译的代码片段。但我同意这可能需要由具有适当洞察力的人更详细地解释。 - tripleee
4个回答

28

假设使用的是Common Lisp。

为什么需要或者可能需要井号引用(sharp-quote)?

如果您想从函数名称计算函数对象(特别是如果您想引用词法绑定)或lambda表达式,则需要特殊运算符FUNCTION,或更短的#'

lambda表达式返回函数对象,

不是这样的。Lambda表达式甚至无法被评估。

在Common Lisp中,它看起来像(lambda () nil)可以被评估。但事实并非如此。

在CLtL1之后的某个时候,添加了一个宏LAMBDA,将其展开为(FUNCTION ...)表达式。该宏节省了一些打字,并使代码看起来更像Scheme。让我们检查这个LAMBDA宏:

CL-USER 17 > (macroexpand-1 '(lambda () ()))  ; not a lambda expression
(FUNCTION 
  (LAMBDA NIL NIL)                            ; <- this is a lambda expression
)
T

这意味着如果您评估(lambda () ()),将会发生以下情况:
  • LAMBDA是一个宏。因此,该表单被扩展为(function (lambda () ()))
  • (function (lambda () ())) -> FUNCTION是一个特殊操作符。它返回一个函数对象。
  • ->

如果您写:#'(lambda () ())(function (lambda () ())),则将跳过宏扩展。

好的,现在来点奇怪的:

CL-USER 18 > (lambda () ())   ; <- this is not a lambda expression.
                              ;    it's a macro form, see above 
#<anonymous interpreted function 40600009FC>

因为上述代码是宏的形式,所以它会先被展开再进行评估。

CL-USER 19 > (function          ; <- this is a special form
              (lambda () ())    ; <- this is a lambda expression
              )
#<anonymous interpreted function 4060000C0C>

这里是一个lambda表达式。在特殊的运算符FUNCTION内部,该形式不会被宏展开或类似地处理。

CL-USER 20 > (                  ; <- this is a function call
              (lambda () ())    ; <- this is a lambda expression
              )

上面展示了一个lambda表达式。其中((function (lambda () ())))在Common Lisp中是无效的。在函数调用的函数位置,Common Lisp期望的是函数名或lambda表达式,而不是需要被求值的内容。

而且sharp quoting从名称返回函数对象。

FUNCTION(其中#'是一种简短的符号)从函数名称lambda表达式返回函数对象。
请查看文档:FUNCTION

我也听到过关于lambda表达式是否是名称的矛盾信息 - 特别是On Lisp与标准相矛盾,但他的代码似乎也可以工作,这也与标准相矛盾。

如果您想听到最后的话,请阅读ANSI CL标准。或者使用Common Lisp Hyperspec,它是可在Web上阅读的,并源自标准。
阅读On Lisp肯定是有用的,但它可能没有完全遵循ANSI CL的措辞或语义。On Lisp是在CLtL2之后但在ANSI CL之前出版的。 在编写代码时,这实际上意味着什么? 如果你像我这样老,CLtL1是你读过的Common Lisp中最后的东西,那么写代码时就像这样:
(mapcar #'(lambda (x) (* x x)) '(1 2 3))

如果你年纪更大并且是在Scheme语言下成长的,或者你更年轻但已经阅读过Common Lisp Hyperspec,那么你可能想要写:
(mapcar (lambda (x) (* x x)) '(1 2 3))

但是,对于所有人来说,当涉及到函数名称时,这是默认的写法:
(mapcar #'sin '(1 2 3))

(lambda ...) 在 (function ...) 特殊形式中被视为 lambda 表达式而不是宏吗?因为我不确定,所以一直在使用 #'. - Makketronix

7
简而言之,你不必对lambda进行尖括号引用,因为它是一个宏,可以扩展为(function (lambda ...)),而且是function做了所有的魔术。
唯一不需要function的意义在于你不必亲自输入它。function而不是lambda是基本运算符。Lambda表达式本身只是普通的列表(function知道如何将其转换为函数对象),而lambda只是普通的宏。所以你把事情搞反了:lambda表达式返回函数对象并不能使function变得无关紧要,因为lambda是用function来解释的。
另一个问题是,在Emacs Lisp中,以lambda开头的列表可以直接被视为函数,而不需要经过function。这在CL中不是这样的(虽然可以通过(coerce some-list 'function)进行显式转换)。

6
这一切都与历史有关。 (lambda ...) 只是 #'(lambda ..) 的语法糖。早期版本的Common Lisp没有将 lambda 定义为宏。例如,如果你阅读Peter Norvigs essay about Quines(PD,第2页),你会看到他明确说明需要创建这样的宏:
(defmacro lambda (args &body body)
  "Allow (lambda (x) ...) instead of #'(lambda (x) ...)"
  `#'(lambda ,args .,body))

因此,当今天写(lambda ...)时,标准宏会将其重写为#'(lambda ...)

On Lisp是一本旧书,可能在宏成为标准之前首次出版。另外,Paul Graham可能习惯于编写#'(lambda ...)并坚持沿用。

我看到计算机书籍的后续版本通常在符合新标准时尽可能少地进行更改。我不确定这是否是好事。


只是澄清一下你所说的。我现在才开始学习这个(有点晚)。(lambda ...)宏仅仅是语法糖,不是任何(lambda ...)。我这么说的原因是:你可以做(function(lambda()3)),但不能做(function#'(lambda()3)),因为(lambda()3)是一个lambda表达式,在这里不是宏。(请参见Rainer的帖子,这让我理解了这一点)。 - Makketronix

2
“function”关键字在不同的Lisp语言中似乎有不同的含义。
在LISP 1.5中,它用于使用“funarg”设备创建闭包。参考资料:http://c2.com/cgi/wiki?DynamicClosure和LISP 1.5程序员手册附录B。
在MacLisp中,它被用作提示编译器的方式,表示lambda表达式可以编译为代码。参考资料:The Pitmanual, 7. Definitional Forms。它并没有用来创建闭包;而是使用“*function”特殊形式实现了类似的功能。参考资料:The Pitmanual, 3. The Evaluator
在Emacs Lisp中,它是一个向编译器提供的提示,也可以创建词法闭包。参考:Emacs Lisp参考手册,12.7 匿名函数

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接