为啥你可以重新定义 `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 中,lambda
和 define
是编译器阶段的***绑定。 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失效,就得重新登录系统,这是为啥?!