为啥你可以重新定义 `lambda`?

Posted

技术标签:

【中文标题】为啥你可以重新定义 `lambda`?【英文标题】:Why can you redefine `lambda`?为什么你可以重新定义 `lambda`? 【发布时间】:2018-01-13 15:42:01 【问题描述】:

我不明白这两个 Scheme 程序之间的以下行为:

计划 1

(define a
  (begin
    (display "hmmm")
    (newline)
    lambda))

这个程序使用scheme test.ss 运行,在lambda 行给我一个语法错误,而没有打印出字符串"hmm"

方案 2

(define lambda 5)
(define a (+ 1 2 lambda))

这里的最终结果是a 等于8


第一个程序中的行为是我期望在两个程序中的行为。令我困惑的是为什么第二个程序不会因语法错误而失败。显然我正在重新定义lambda,但我认为这会在代码实际运行之前失败并出现语法错误。在我看来,要知道这是 not 语法错误,您需要实际运行程序,但如果这是行为,那么我希望第一个程序在出错之前显示字符串出去。

简而言之,为什么第一个程序会导致语法错误,而第二个程序不会?

【问题讨论】:

我怀疑原因完全是卫生宏,但我想确认一下,我认为这可能对未来的学习计划者有用 重新定义lambda 完全没问题;这只是一个名字。在没有正确语法的情况下使用绑定到其原始值的lambda 是一个语法错误。这里的关键是lambda绑定 的,而不是它的名字。 Scheme 没有“关键字”。 @AlexisKing 在这种情况下不应该在第一个程序中执行display 之前出现任何错误吗? 语法错误是编译时错误,而不是运行时错误。代码从不执行任何东西,因为它甚至不编译。 Scheme 是一种完全词法作用域的语言,词法作用域的特性之一是可以在编译时完全确定绑定。 lambda 本质上可以被认为是一个宏,它是一个编译时绑定,但define(与define-syntax 不同)定义了一个运行时绑定。宏通常旨在用于各种不规则的“形状”,滥用会引发语法错误,但运行时值非常统一,它们遵循通常的 Scheme 语法规则。 【参考方案1】:

正如亚历克西斯所说,重新定义 lambda 是完全可以的。

在您的第一个示例中,您在参数a(display "hmmm")(newline)lambda 上调用过程define。由于 Scheme 是一种急切的语言,它会在执行前尝试评估每个参数,这很可能是它在评估 lambda 时失败的原因(因为在这种情况下,lambda 是一个需要一些参数 ID 的过程:@ 987654321@).

第二个示例成功了,因为在对参数 a 和求和 (+ 1 2 lambda) 调用 define 时,求和解析为有形类型(因为 lambda 已被重新定义为整数)。

希望对您有所帮助。

【讨论】:

感谢您的尝试,但正如我在 cmets 中所了解的那样,我不明白在编译时如何知道 lambda 被重新定义。每段代码的实际评估对我来说很有意义。【参考方案2】:

在 Scheme 中,lambdadefine 是编译器阶段的***绑定。 define 正好需要两个操作数,既然你提供了 4 它应该首先对此做出反应。所以让我们先解决这个问题:

(define a 
  (begin 
    (display "hmmm")
    (newline)
    lambda)))

现在您收到关于 lambda 的错误消息。它是创建过程的原始形式,因此编译器认为您使用它错误:

(lambda (x) (+ x x)) ; implementation of double

如果您将lambda 定义为变量,则不会发生错误,因为即使这是制作过程的方式,您也可以制作具有相同名称的变量。

(define lambda 10)
(define a 
  (begin (display "hmmm")
  (newline)
  lambda))
; ==> 10 (and it printed "hmmm")

编译器知道代码的词法性质。它确切地知道定义了哪些绑定,哪些绑定是水平的,以及在哪个阶段。***lambda 不再可用。

现在在你的第二个程序中定义lambda 然后使用它,就像我的最后一个例子一样。

请注意,在 R5RS 中,编译器假定重新定义 og 库和原始过程是兼容的,并且可能会不断折叠:

(define (+ a b)
  (string-append a b))
(display (+ 4 5)) ; displays 9

为了辩护,我的+ 不兼容违反了 R5RS 报告。如果不是***就可以了:

(let ((+ (lambda (a b) (string-append a b))))
  (+ 4 5)) ; no loinger top level, will signal n error!

【讨论】:

感谢您发现begin 的错误,这并没有改变我的问题,但这是一个愚蠢的错误。您提到编译器知道代码的词法性质,这是让我感到困惑的部分。您是否有关于其具体工作原理的推荐资源? @jcolemang 如果您对阅读有关该主题的源代码或论文不感兴趣,R6RS reports part about macros 解释说关键字(宏)可以隐藏绑定,反之亦然。如果您对编译器感兴趣,您可能需要查看Matt Mights material 或阅读编译器的源代码。 Chez 最近被改写为nanopass compiler。 我对编译器的世界还是很陌生,所以源代码还是有点过头了。稍后我会研究这些,非常感谢!

以上是关于为啥你可以重新定义 `lambda`?的主要内容,如果未能解决你的问题,请参考以下文章

使用 lambda 在单个实例上重新定义单个 ruby​​ 方法

调用将 lambda 函数加在一起的自定义函数时内核重新启动

为啥你永远不应该重新加载模块? [复制]

vs2008编辑aspx页面时,经常会session失效,就得重新登录系统,这是为啥?!

div+css问题,height高度自适应后,重新定义div的height无效?为啥?

为啥要在 didSelectRowAtIndexPath 中重新定义 pListPath?