Lisp评估let语句

时间:2009-03-17 03:03:59

标签: lisp scheme interpreter

我正在编写一个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中的最后一个表达式,我还有什么理由可以评估它吗?它们似乎永远不会影响最后评估的结果。

3 个答案:

答案 0 :(得分:6)

实际上你不能“删除”除最后一个语句之外的所有语句,因为之前的语句可能是非终止的。例如:

(define (func) (func))

(let ()
  (func) ;; does not return
  1)

如果您将(func)保留为未评估状态,则会得到错误的结果(即1),而您应该得到非终止计算。

另一个问题是call / cc(call-with-current-continuation)(是的,它属于函数子集)可以用来实际从非尾部计算返回职位,例如:

(call-with-current-continuation
  (lambda (ret)
    (let ()
      (ret 3)
      4)))

将返回 3 而不是4.这仍然是纯粹的功能。

请注意,(let () x y z)等同于单一陈述形式(let () (begin x y z)),所以真正的问题是如果您需要begin:)

答案 1 :(得分:2)

你是对的(差不多):如果你正在实现一个纯函数的Scheme子集(即没有set!set-car!set-cdr!)那么任何表达式,但是最后一个一个let将丢弃它们的返回值,并且由于你保证不会有副作用,所以没有危险,默默地忽略它们。

然而,您需要考虑一个小案例,即前面的表达式是define s:

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

这既合法又实用。但是,有一些好消息 - 在一个区块内(如let),您必须将所有define放在顶部。如同,这不被视为合法方案:

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

这意味着在评估一个块时,您所要做的就是扫描顶部的define并将它们包装到等效的letrec表达式中,然后继续忽略除最后一个表达式之外的所有表达式(然后你会回来)。

编辑 antti.huima对call / cc提出了一个很好的观点。如果你要在实现中包含continuation,那么你真的不能对什么时候进行评估做出很多假设。

答案 2 :(得分:1)

好的,let只是创建一个绑定,就像define一样。那里没有任何改变像set!那样的绑定变量。现在,想一想你的名字的范围是什么:是你绑定到4的'(+ a b)a a`的the same as the? (提示:不。)

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

<强>更新 哦,我离开了一点。你是对的(+ a b)调用没有持久的效果,但是在一般情况下你不能认为这是真的,你无法通过单独检查程序文本来确定它是否成立。 (考虑:可能还有其他功能代替“+”。)但是,如果您认为在不评估各种let条款的情况下得到正确的结果,则可以使用其他函数。不明白它还在尝试做什么。