【问题标题】:Why sharp quote lambda expressions?为什么要使用尖引号 lambda 表达式?
【发布时间】:2015-03-30 03:33:38
【问题描述】:

这是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 中,这样的列表是一个有效的表达式,它的计算结果是一个函数对象。”

这个问题被标记为 common- 和 e-lisp,因为我试图理解两者的理论模型,并通过学习它们的差异来学习它们。 (更实际地,我正在尝试学习 elisp,但在 Common Lisp 上找到了很多好的资源,所以我都在学习这两个)。

【问题讨论】:

  • 另见the Elisp Reference manual,这意味着function 引用有助于字节编译器识别应该编译的代码片段。但我同意这可能应该由具有适当洞察力的人更详细地解释。

标签: lambda lisp elisp common-lisp sharp-quote


【解决方案1】:

假定为 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 () ())) -> 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,它是网络可读的并且源自标准。

在 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 ...) 被视为 lambda 表达式,而不是仅在 (function ...) 特殊形式中的宏?因为我不知道我一直在使用#'。
【解决方案2】:

简而言之,您不必对lambda 进行尖锐引用,因为它是一个扩展为(function (lambda ...)) 的宏,而function 发挥了所有作用。

不需要function 的唯一意义是您不必自己输入。基本运算符是function,而不是lambda。 Lambda 表达式本身只是普通的列表(function 知道如何变成函数对象),lambda 只是一个普通的宏。所以你得到了一些相当倒退的东西:lambda 表达式返回函数对象并没有排除function,因为lambda 是根据function 解释。

另一个问题是,在 Emacs Lisp 中,以 lambda 开头的列表可以直接视为函数,而无需经过 function。在 CL 中情况并非如此(尽管可以以 (coerce some-list 'function) 的形式进行显式转换)。

【讨论】:

    【解决方案3】:

    这一切都与历史有关。 (lambda ...) 只是 #'(lambda ..) 的语法糖。 Common Lisp 的早期版本没有将lambda 定义为宏。例如。如果您阅读Peter Norvigs essay about Quines (PD, page2),您会看到他明确指出您需要创建如下宏:

    (defmacro lambda (args &body body)
      "Allow (lambda (x) ...) instead of #'(lambda (x) ...)"
      `#'(lambda ,args .,body))
    

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

    On Lisp 是一本旧书,它可能在宏成为标准之前就已首次发布。也可能是 Paul Graham 习惯了写#'(lambda ...) 并且一直坚持下去。

    我发现,计算机书籍的较新版本在符合较新标准时通常会尽可能少地更改。我不确定这是一件好事。

    【讨论】:

    • 只是澄清你在说什么。我正在研究这个(有点晚了)。 (lambda ...) MACRO 只是语法糖,而不是任何 (lambda ...)。我这么说的原因是:你可以做 (function (lambda () 3)) 但不能做 (function #'(lambda () 3)) 因为 (lambda () 3)) 是一个 lambda 表达式,而不是这里的宏。 (请参阅 Rainer 的帖子,这有助于我理解这一点)。
    【解决方案4】:

    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.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-02
      • 2011-10-19
      • 2015-12-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多