这是一种在 On Lisp 中经常使用的技术,它位于Common Lisp上:
> (mapcar #'(lambda (x) (+ x 10))
'(1 2 3))
(11 12 13)
为什么需要锐利报价甚至可能? lambda表达式返回函数对象,而尖锐的引用从名称返回函数对象。我也听过有关lambda表达式是否是名称的矛盾信息 - 特别是 On Lisp 与标准相矛盾,但他的代码似乎也与标准相矛盾。
在elisp it seems it's not needed at all中。令我惊讶的是,这里没有讨论词法范围,因此不清楚为什么它应该与Common Lisp完全不同。但是this answer说没有引用的lambda表达式没有任何意义,因此,具有讽刺意味的是,有可能引入不带引号的lambda表达式作为引用的语法糖。但这与the elisp manual's claim相矛盾,在Emacs Lisp中,这样的列表是一个有效的表达式,它可以计算出一个函数对象。"
这个问题被标记为普通和e-lisp,因为我试图理解两者的理论模型,并通过了解它们的差异来学习它们。 (更实际的是,我试图学习elisp,但在Common Lisp上找到了很多很好的资源,所以我很快就学会了这些。)
答案 0 :(得分:18)
Common Lisp假设。
为什么需要锐利报价甚至可能?
如果要从函数名称计算函数对象(特别是如果要引用词法绑定)或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 () ()))
- &gt; 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表达式,但不需要评估。
和尖引号从名称返回函数对象。
FUNCTION
,#'
是一个简短表示法,从函数名或 lambda表达式返回函数对象。
请参阅文档:FUNCTION。
我也听过有关lambda表达式是否是名称的矛盾信息 - 特别是On Lisp与标准相矛盾,但他的代码似乎也有效,这也与标准相矛盾。
如果您想听到最后一个字,请阅读ANSI CL标准。或者使用Common Lisp Hyperspec,它是Web可读的并且源自标准。
On Lisp绝对有用,但它可能不完全遵循ANSI CL的措辞或语义。关于Lisp是在CLtL2之后发布的,但是在ANSI CL之前发布。
编写代码时实际意味着什么?
如果你像我一样年老,而且你读过Common Lisp的最后一件事是CLtL1,那么就写下这样的代码:
(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))
答案 1 :(得分:6)
简而言之,您不必尖锐引用lambda
,因为它是一个扩展为(function (lambda ...))
的宏,并且function
可以完成所有魔法。
唯一不需要 另一个问题是,在Emacs Lisp中,以function
的感觉就是你不必自己打字。它是function
,而不是lambda
,它是基本的运算符。 Lambda表达式本身只是普通列表(function
知道如何变成函数对象),而lambda
只是一个普通的宏。所以你得到了相反的东西:lambda表达式返回函数对象并不能消除function
,因为lambda
用 function
来解释。 / p>
lambda
开头的列表可以直接作为函数处理,而不需要通过function
。在CL中不是这种情况(尽管可以(coerce some-list 'function)
)形式进行显式转换。
答案 2 :(得分:4)
这一切都与历史有关。 (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是一本旧书,它可能是在宏成为标准之前首次发布的。也许保罗格雷厄姆习惯于写#'(lambda ...)
并坚持下去。
我已经看到,在符合更新的标准时,后期版本的计算机书籍通常会尽可能少地改变。我不确定这是件好事。
答案 3 :(得分: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 Reference Manual, 12.7 Anonymous Functions。