Kai*_*Kai 8 lisp scheme interpreter
我正在编写一个Scheme解释器,我面临一个有效的let语句,例如:
;; should print 7
(let ((a 4) (b 3))
(let ((a (* a a))
(b (* b b)))
(+ a b)
(- a b)))
Run Code Online (Sandbox Code Playgroud)
我的解释器只实现了Scheme的一个纯函数子集,因此不存在set!等副作用.在纯函数式语言中,为什么要在let语句中允许多个表达式,如上所述?
在编写我的翻译时,除了let中的最后一个表达式,我还有什么理由可以评估它吗?似乎它们永远不会影响最后评估的陈述的结果.
实际上你不能"丢弃"除最后一个语句以外的所有语句,因为之前的语句可能是非终止的.例如:
(define (func) (func))
(let ()
(func) ;; does not return
1)
Run Code Online (Sandbox Code Playgroud)
在这里,如果你没有(func)评估,你得到错误的结果(这是1),而你应该得到非终止计算.
另一个问题是call/cc(call-with-current-continuation)(是的,它属于功能子集)可用于实际从非尾部位置返回计算,例如:
(call-with-current-continuation
(lambda (ret)
(let ()
(ret 3)
4)))
Run Code Online (Sandbox Code Playgroud)
这将返回3而不是4.这仍然是纯粹的功能.
注意BTW (let () x y z)等同于单语句形式(let () (begin x y z))所以真正的问题是如果你需要begin:)
你是对的(几乎):如果你正在实现Scheme的纯功能子集(即没有set!,,),那么除了a中的最后一个表达式之外的任何表达式都将丢弃它们的返回值,并且因为你保证不会set-car!有副作用,默默地忽略它们并没有危险。set-cdr!let
但是,您需要考虑一种小情况,即前面的表达式为defines 时:
(let ((x 3))
(define y 4)
(+ x y))
Run Code Online (Sandbox Code Playgroud)
这既合法又实用。然而,有一些好消息 - 在一个块(如 a let)内,您必须将所有defines 放在顶部。例如,这不被视为合法方案:
(let ((x 3))
(+ 2 3)
(define y 4)
(+ x y))
Run Code Online (Sandbox Code Playgroud)
这意味着在评估一个块时,您所要做的就是扫描顶部的defines 并将它们包装到等效letrec表达式中,然后继续忽略除最后一个表达式之外的所有表达式(然后您将返回该表达式)。
编辑: antti.huima对 call/cc 提出了很好的观点。如果您要在实现中包含延续,那么您实际上无法对何时评估事物做出很多假设。
| 归档时间: |
|
| 查看次数: |
1453 次 |
| 最近记录: |