CommonLisp中的lambda表达式是宏还是标记?
我正在尝试通过《Common Lisp:符号计算的温和介绍》一书来学习 Common Lisp 。此外,我正在使用 SBCL、Emacs 和 Slime。
在第 7 章中,作者提出以下建议lambda expressions:
这让我感到困惑,因为 SBCL 的 REPL 返回:
CL-USER> #'lambda
#<CLOSURE (:MACRO LAMBDA) {1000D736DB}>
显然,作者使用了Lisp Works(不是 100% 确定)。我认为这与上述差异无关。不过,我觉得还是提一下比较好。
我的 SBCL 的 REPL 也返回macro了众所周知的宏,例如and:
CL-USER> #'and
#<CLOSURE (:MACRO AND) {1000D7365B}>
请注意,具有“普通”功能的行为例如append是不同的:
CL-USER> #'append
#<FUNCTION APPEND>
这里的这篇文章似乎稍微触及了lambda 表达式的非单一性质。但是,它没有提到任何有关标记的内容。
我是否遗漏了 lambda 表达式的本质?
回答
一种思考方式是认识到在 CL 中获得词法明显函数值的唯一方法是特殊运算符function:如果您想获得与foo当前词法环境关联的函数,您必须说(function foo):
(flet ((foo (x)
x))
(function foo)
例如。function特殊运算符也是如此,它可以让您查看函数命名空间中的内容。 function有一些语法糖,也就是#',以同样的方式quote(我不会在下面使用它)。您可以将函数应用程序(f x y)视为近似于(<funcall> (function f) x y),其中<funcall>有一些神奇的东西不会像那样被替换。
但是您也需要匿名函数,而且,您也可以使用匿名函数,function其参数是lambda 表达式:
lambda 表达式n. 可以在某些上下文中代替函数名称使用的列表,通过直接描述其行为而不是通过引用已建立的函数名称来间接表示函数;它的名字来源于它的第一个元素是符号 lambda。– CLHS
所以匿名函数表示为(function (lambda (...) ...))。如果您愿意,您可以将其(lambda (...) ...)视为函数的“名称”。(我认为人们不喜欢这个,因为它干扰了匿名函数的想法,但很明显,表单的可能函数“名称”有一组可数的集合,(lambda (...) ...)你甚至可以枚举我认为的这个集合(这样做会像枚举有理数一样繁琐)。
由于函数应用程序中的函数位置已经在函数的命名空间中进行了解释,因此((lambda (...) ...) ...)表示匿名函数应用程序:它或多或少与(funcall (function (lambda (...) ...) ...)(再次参见CLHS)相同。
这就是 CL 在 1980 年代的某个时候的样子。
然后有一些我不确定的历史混乱:我记得它发生过,但我不记得顺序。首先,在一个Lisp-1,你不需要任何的这个function东西:形式car表示其获取函数车一的缺点,你不需要说了(function car)。并且类似地(lambda (...) ...)表示一个函数。除此之外,还有另一个提议的 Lisp 标准,它是/是 ISLisp,它可能在这里。尽管 ISLisp 不是 Lisp-1,但它确实具有(lambda (...) ...)表示函数的形式。
人们希望 CL 能够与 ISLisp 兼容,这意味着(lambda (...) ...)应该表示一个函数。每个人实际上所做的是偷偷添加一个定义,例如:
(defmacro lambda (args &body forms)
`(function (lambda ,args ,@forms)))
但是,关键的是,你不能做的可移植性,在CL,因为lambda是cl:lambda和你不许在CL包重新定义的东西。人们无论如何都这样做了,但结果是他们的程序不可移植,并且经常不得不用特殊的魔法装饰来解锁和重新锁定CL包裹。
好吧,解决方案是语言必须提供这样的宏。在 CLtL1 定义的语言和最终标准之间的某个时间发生了这种情况,所以现在lambda有一个宏定义,其扩展是(或多或少:实现可能被允许做特殊的事情)明显的扩展:
(lambda (x) x)
-> #'(lambda (x) x)
所以在现代 CL 中:
- 列表开头
lambda表示('名称')一个函数,形式为 like(function (lambda (...) ...),也在形式的函数位置中,所以((lambda (x) x) 1),比如说。 lambda被定义为一个宏,它扩展为(function (lambda (...) ...))更易于使用。
注意:我认为您正在使用的实现允许对宏执行它所做的操作,但您不应该依赖于此:
在函数形式出现的词法环境中,对不表示函数的函数名使用函数是错误的。具体来说,在表示宏或特殊形式的符号上使用函数是错误的。出于性能原因,实现可能会选择不发出此错误信号,但禁止实现将发出错误信号的失败定义为有用的行为。– CLHS
您真的应该使用macro-functionwhich 来告诉您是否有宏定义。