我正在编写一个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都会有自己的返回值扔掉,并自你保证不会有副作用,有一个在默默忽略他们没有生命危险。

<强>然而,有你需要考虑一个小的情况下,那就是当上述表达式是defines:

(let ((x 3))
  (define y 4)
  (+ x y))

这是合法和功能。然而,有一些好消息 - 块内部(如let)你必须有在顶部所有的defines。正如,这还不算是合法的方案:

(let ((x 3))
  (+ 2 3)
  (define y 4)
  (+ x y))

这意味着,评估块时,所有必须要做的就是扫描顶部defines和将它们包装成等效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 thea` 你绑定到 4 了吗?(暗示:不。)

这里真正的要点是,即使在像这样的棘手情况下,你也需要正确行事:范围和绑定规则简单且定义明确,并且执行类似这样的操作,这 看起来 令人困惑,只是他们的结果。这很方便,因为通过具有本地词法范围的绑定 let, ,即使存在不正当的副作用,您也可以编写更清晰的程序。

更新哦,我漏掉了一点。你是对的 (+ a b) 调用没有持久效果,但是在一般情况下您不能假设这是真的,并且您无法仅通过检查那里的程序文本来确定它是否为真。(考虑:那里可能有其他功能来代替“+“。)但是,在其余部分,如果您认为无需评估各种因素就能得到正确的结果 let 子句,你还不明白它想做什么。

scroll top