[ML, Scheme] 无穷表, Stream
本贴内容来自 《ML 程序设计语言》(Paulson) 的第 5 章:函数和无穷数据。
在惰性求值语言中如 Haskell 中,惰性是内建的,所以能轻易定义无穷数据。而在严格求值的语言中,要实现惰性求值,就要花费额外的工夫。SICP 第三章最后一节 Strem,讨论的就是这个问题。本贴给出几个在 ML 中实现无穷表的例子,可以与 SICP 对照着看一下:它们的手段是类似的。
[ 本帖最后由 win_hate 于 2008-11-7 16:03 编辑 ]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
书本的习题还提供了一种 “更具惰性” 的实现
复制代码
这个实现有两种而不是一种类型:seq 和 seqnode。每次对 seq 求值得到一个 node, 而一个 node 是一个 'a 类型和 seq 的对。
cons
复制代码
试下先:
复制代码
bump! 出错了。
cons 1 Nil 太顺手了,lisp 中是这样,前面两个 ML 也这样。但这里情况不同。
在前面的例子中, Nil 是个 'a seq, 即空表(流)。但在这个例子中 Nil 是个 'a seqnode. cons x xq 调用 Cons (x, xq),所以 xq 应该是个 'a seq 才对。
自己做一个空表:
复制代码
再来
复制代码
这次好像对了,但如何把数据提取出来呢?这比前两个例子要稍微罗唆一点:
复制代码
fst, rst 是处理 'a seqnode 的,而 car, cdr 则是处理 'a seq 的。
效果:
复制代码
form 和 take 和自然数集
复制代码
复制代码
SICP 第四章讨论如何用 Scheme 的内建机制实现一个 Scheme 解释器。其中 4.2 讨论惰性求值,也就是用严格 Scheme 解释器实现一个惰性 Scheme 解释器。
我一直挺好奇 PLT Scheme 的 lazy scheme 是怎么实现的。
没有。我有两个猜测
这两个猜测是矛盾的,不知道哪个对。
win_hate 有研究过 PLT DrScheme 中的 lazy scheme 么?
没人拍砖,自己拍一下。
上面构建的流,流首总是被预先求出的,所以这个方案不够懒惰。书本的习题提供了另一个方案
复制代码
此时流首和后继都是通过一个无参函数返回的。cons, car, cdr 修改如下:
复制代码
对一个有穷表试一下:
复制代码
构造一个无穷表生成函数 from,配套一个 take
复制代码
效果:
复制代码
下面我们来看一个 ML 实现惰性表的例子
复制代码
对 Cons (x, y), 利用 y() 就可以得一个新表。这个新表正好是无穷序列 Cons(x,y) 中 x 之后的部分。
表头插入函数 cons
复制代码
car 和 cdr
复制代码
既然定义了表尾 Nil,这套机制应该可以用在有穷表上:
复制代码
构造一个无穷表? 比如 from k,生成序列:k, k+1, k+2, ...
看上去应该是:
复制代码
但这是不行的,应为 from (k+1) 现在是 cons 的参数。cons 是 ML 中一个自定义函数,它的求值方式是严格的。在 Scheme 中有宏,在 ML 中有 datatype
复制代码
直接用 'a seq 的构造子 Cons 就可以了。
试一下
复制代码
再来个 take,
复制代码
复制代码
(先到这里,欢迎拍砖).
[ 本帖最后由 win_hate 于 2008-11-7 11:37 编辑 ]
在 Scheme 中,惰性求值是通过 delay 来实现的。如果想延迟表达式 E 的求值,可以构造找一个无参函数 (lambda () E),在需要时,调用这个函数就可以得到 E 的值.
困难在于,如何构造 (lambda () E) 呢?如果把 delay 实现为函数,比如 (delay (E) (lambda () E)),这样是不行的。因为此时 E 是 delay 的参数,按 scheme 的严格求值方式,在构造 (lambda () E) 之前,E 已经被求值了。
绕过这个困难的一个方法为使用特殊形式,即宏。下面是一个“山寨版” 的 delay 和 force,后者取消延迟
复制代码
SICP 中还提到了一些优化手段,不过在这里不关心它们。现在我们知道,SICP 中给出的惰性手段就是 delay,它有两个要点:宏和无参函数。
[ 本帖最后由 win_hate 于 2008-11-7 10:37 编辑 ]