【发布时间】:2020-12-13 00:29:26
【问题描述】:
当我被困在作业 6 的第 5 部分时,我一直在经历出色的 CIS 194 course。它围绕着在没有任何可分性测试的情况下实现 ruler function。
我发现可以通过不断地将累加器与无限列表中的值穿插来构建标尺函数。
nats = [0,1,2,3,..]
[3]
[2,3,2]
[1,2,1,3,1,2,1]
[0,1,0,2,0,1,0,3,0,1,0,2,0]
然后我尝试为Stream 数据类型实现此算法,这是一个没有nil 的列表
data Stream a = Cons a (Stream a)
streamToList :: Stream a -> [a]
streamToList (Cons x xs) = x : streamToList xs
instance Show a => Show (Stream a) where
show = show . take 20 . streamToList
streamFromSeed :: (a -> a) -> a -> Stream a
streamFromSeed f x = Cons x (streamFromSeed f (f x))
nats :: Stream Integer
nats = streamFromSeed succ 0
interleave x (Cons y ys) = Cons x (Cons y (interleave x ys))
foldStream f (Cons x xs) = f x (foldStream f xs)
ruler = foldStream interleave nats
正如预期的那样,由于我试图从右侧折叠,所以我遇到了 stackoverflow 错误。但是,我很惊讶地看到相同的算法适用于普通的无限列表。
import Data.List
interleave x list = [x] ++ (intersperse x list) ++ [x]
ruler = take 20 (foldr interleave [] [0..])
我错过了什么?为什么一种实现有效而另一种无效?
【问题讨论】:
-
foldr不完全从右侧折叠,它关联到右侧,即foldr f z [1 .. 3]=1 `f` (2 `f` (3 `f` z))=f 1 $ f 2 $ f 3 $ z。如果f在其第二个参数中是惰性的,它可以在无限流上工作并逐渐产生结果。同样,懒惰的foldl(而不是严格的foldl')可以在其第一个参数中使用懒惰的函数。问题在于使用了错误的严格性——在参数 1 到foldl或在参数 2 到foldr中给出函数 strict——所以它需要一直强制到流的末尾才能开始产生结果。对于无限的流,没有这样的结束。