【问题标题】:Are lambda expressions macros or markers in Common Lisp?Common Lisp 中的 lambda 表达式是宏还是标记?
【发布时间】:2021-08-11 00:11:49
【问题描述】:

我正在尝试通过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>

这篇here 的帖子似乎略微触及了 lambda 表达式的非单一性。然而,它没有提及任何关于标记的内容。

我是否遗漏了一些关于 lambda 表达式本质的东西?

【问题讨论】:

  • Common Lisp 中没有标记的概念。它只是在书中用作解释词。 LAMBDA 既是一个宏,又用作LAMBDA 表达式的第一个元素。因此可以说它“标记”了一个 lambda 表达式,但这个词本身并不是 Common Lisp AFAIK 中的技术术语。在编写本书第一版时,可能使用的是较旧版本的 Common Lisp,当时没有 LAMBDA 宏。后来添加到 Common Lisp 中

标签: lambda common-lisp read-eval-print-loop markers lisp-macros


【解决方案1】:

思考这个问题的一种方式是意识到在 CL 中获得一个词法上明显的函数值的唯一方法是特殊运算符 function:如果你想获得与 foo 关联的函数当前的词汇环境你不得不说(function foo):

(flet ((foo (x)
         x))
  (function foo)

例如。所以function 是一个特殊的运算符,它可以让你看到函数命名空间中的内容。 function 有一些语法糖,即#',就像quote 一样(我不会在下面使用它)。您可以将函数应用程序(f x y) 视为近似于(&lt;funcall&gt; (function f) x y),其中&lt;funcall&gt; 是一个神奇的东西,它不会反过来被那样替换。

但您也需要匿名函数,而且,您也可以使用 function,其参数是 lambda 表达式:

lambda 表达式 n.在某些上下文中可以用来代替函数名称的列表,通过直接描述函数的行为而不是间接地通过引用已建立函数的名称来表示函数;它的名字来源于它的第一个元素是符号 lambda。 – CLHS

所以匿名函数表示为(function (lambda (...) ...))。如果愿意,您可以将(lambda (...) ...) 视为函数的“名称”。 (我认为人们不喜欢这个,因为它干扰了匿名函数的概念,但很明显,有一组可数的可能的函数“名称”,形式为 (lambda (...) ...),你甚至可以枚举这组我认为(这样做会很繁琐,就像枚举有理数很繁琐)。

由于函数应用程序中的函数位置已经在函数的命名空间中解释,((lambda (...) ...) ...) 表示匿名函数应用程序:它或多或少与(funcall (function (lambda (...) ...) ...) 相同(再次参见CLHS)。

这就是 CL 在 1980 年代某个时候的样子。

然后有一些我不确定的历史混乱:我记得它发生过,但我不记得顺序。首先,在 Lisp-1 中,你不需要任何 function 的东西:car 表示获取 cons 的 car 的函数>,你不需要说(function car)。同样(lambda (...) ...) 表示一个函数。除此之外,还有另一个提议的 Lisp 标准,即 ISLisp,可能是here。虽然 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 开头的列表表示('names')一个函数,其形式类似于(function (lambda (...) ...),也位于表单的函数位置,例如((lambda (x) x) 1)。
  • lambda 被定义为一个扩展为 (function (lambda (...) ...)) 的宏,这样更易​​于使用。

注意:我认为,您正在使用的实现允许执行它对宏所做的操作,但您不应依赖此:

在函数形式出现的词法环境中不表示函数的函数名上使用函数是错误的。具体来说,在表示宏或特殊形式的符号上使用函数是错误的。出于性能原因,实现可能会选择不发出此错误信号,但禁止实现将发出错误信号的失败定义为有用的行为。 – CLHS

你真的应该使用macro-function,它会告诉你是否有宏定义。

【讨论】:

    猜你喜欢
    • 2015-12-20
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    • 2014-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    相关资源
    最近更新 更多