题
我正在编写一个Scheme解释器,我面临着一个有效的let语句,例如:
;; should print 7
(let ((a 4) (b 3))
(let ((a (* a a))
(b (* b b)))
(+ a b)
(- a b)))
我的解释器只实现了Scheme的一个纯功能子集,所以不会有set!之类的副作用。在纯函数式语言中,为什么要允许像上面这样的 let 语句中存在多个表达式?
在编写我的解释器时,是否有任何理由我应该评估除 let 中最后一个表达式之外的任何内容?看来它们永远不会影响最后评估的语句的结果。
解决方案
您是对的(几乎):如果你执行计划的一个纯粹的功能子集(即没有set!
,set-car!
,set-cdr!
),那么任何表情,但最后在let
都会有自己的返回值扔掉,并自你保证不会有副作用,有一个在默默忽略他们没有生命危险。
<强>然而,强>有你需要考虑一个小的情况下,那就是当上述表达式是define
s:
(let ((x 3))
(define y 4)
(+ x y))
这是合法和功能。然而,有一些好消息 - 块内部(如let
)你必须有在顶部所有的define
s。正如,这还不算是合法的方案:
(let ((x 3))
(+ 2 3)
(define y 4)
(+ x y))
这意味着,评估块时,所有必须要做的就是扫描顶部define
s和将它们包装成等效letrec
表达,然后进行忽略所有,但最后一个表达式(你会再返回)。
修改强> antti.huima 一>使有关呼叫/ cc的优异的点。如果你要在您的实现延续,你真的不能对事情的时候会评估了许多假设。
其他提示
其实你也不能“落”所有,但最后的语句,因为先前的陈述可以是非终止。例如:
(define (func) (func))
(let ()
(func) ;; does not return
1)
在这里,如果你离开(func)
未计算的,你会得到错误的结果(这是1),而你应该得到非终止计算。
另一个问题是,呼叫/ cc的(呼叫与 - 电流续)(是的,它属于功能子集)可以被用来实际返回从非尾计算位置,例如:
(call-with-current-continuation
(lambda (ret)
(let ()
(ret 3)
4)))
将返回的 3 和不4.这仍然是纯功能性的。
请注意BTW是(let () x y z)
相当于单语句形式(let () (begin x y z))
所以,真正的问题是,如果你需要begin
:)
好的, let
只是创建一个绑定,就像 define
. 。没有什么可以改变绑定变量,比如 set!
会。所以现在,想想什么 范围 你的名字是:是个 a
'(+ a b) 的the same as the
a` 你绑定到 4 了吗?(暗示:不。)
这里真正的要点是,即使在像这样的棘手情况下,你也需要正确行事:范围和绑定规则简单且定义明确,并且执行类似这样的操作,这 看起来 令人困惑,只是他们的结果。这很方便,因为通过具有本地词法范围的绑定 let
, ,即使存在不正当的副作用,您也可以编写更清晰的程序。
更新哦,我漏掉了一点。你是对的 (+ a b)
调用没有持久效果,但是在一般情况下您不能假设这是真的,并且您无法仅通过检查那里的程序文本来确定它是否为真。(考虑:那里可能有其他功能来代替“+
“。)但是,在其余部分,如果您认为无需评估各种因素就能得到正确的结果 let
子句,你还不明白它想做什么。