【问题标题】:Fail to define an infinite stream无法定义无限流
【发布时间】:2016-09-05 09:33:15
【问题描述】:

我正在处理UPENN Haskell Homework 6 Exercise 5,试图定义一个ruler function

0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,...

其中流中的第 n 个元素(假设第一个元素对应于 n = 1)是最大的 power of 2,它均匀地除以 n

我只是想出了一个想法,无需任何可分性测试即可构建它:

data Stream x = Cons x (Stream x) deriving (Eq)

streamRepeat x = Cons x (streamRepeat x)

interleaveStreams (Cons x xs) (Cons y ys) =
    Cons x (Cons y (interleaveStreams xs ys))

ruler =
    interleaveStreams (streamRepeat 0)
        (interleaveStreams (streamRepeat 1)
            (interleaveStreams (streamRepeat 2)
                (interleaveStreams (streamRepeat 3) (...))

其中的前 20 个元素

ruler =
    interleaveStreams (streamRepeat 0)
        (interleaveStreams (streamRepeat 1)
            (interleaveStreams (streamRepeat 2)
                (interleaveStreams (streamRepeat 3) (streamRepeat 4))))

[0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2]

显然我无法手动将其定义为无限,所以我定义了一个infInterStream 来帮助定义这种无限递归:

infInterStream n = interleaveStreams (streamRepeat n) (infInterStream (n+1))

ruler = infInterStream 0

但是现在我在ghci中输入ruler时卡住了,它可能陷入了无限循环。

如果惰性评估有效,则不应该如此。我想知道为什么惰性评估在这里失败。


观察Stream的辅助函数:

streamToList (Cons x xs) = x : streamToList xs

instance Show a => Show (Stream a) where
    show = show . take 20 . streamToList

【问题讨论】:

    标签: haskell stream lazy-evaluation


    【解决方案1】:

    你的交错函数太严格了。以下作品:

    interleaveStreams (Cons x xs) ys = Cons x (interleaveStreams ys xs)
    

    这也有效:

    interleaveStreams (Cons x xs) ~(Cons y ys) = 
        Cons x (Cons y (interleaveStreams xs ys))
    

    原始定义进入无限循环,因为interleaveStreams 要求两个参数必须是Cons 形式。 infInterStream n计算为两个流的交错,第一个可以立即计算为Cons,但第二个也必须先归约为Cons,所以我们递归调用infInterStream (n + 1),它一直在调用本身无限。

    如果interleaveStreams 可以返回Cons a _ 而无需首先强制第二个参数,infInterStream 也可以增量构建结果。

    【讨论】:

    • 你的第一个定义看起来很眼熟,我一定见过。我猜是在 SICP
    【解决方案2】:

    流不需要新类型,我们可以使用 Haskell 的惰性列表代替。正如其他人所指出的,交错的定义必须足够懒惰,以便它可以在测试第二个参数是否非空之前产生输出的第一个元素。这个定义会:

    interleave (x:xs) ys = x : interleave ys xs
    

    如果您希望 interleave 也适用于有限列表,您可以添加等式

    interleave [] ys = ys
    

    还要注意,使用标准前奏中的函数,

    ruler = interleave (repeat 0) (map (+1) ruler)
    

    这里repeat 0 是列表[0, 0, 0, ...]

    【讨论】:

    • 使用单独的流类型使得在本地很明显模式匹配是完整的,所以我认为这样更好。
    • 是的,如果您愿意,您可以这样做:但是您需要为流重新定义所有标准函数(如上面的repeatmap)。妥协是工程的本质。
    • 这就是我们有 Hackage 的原因!代码已经写好了。
    • 为什么我觉得你在挑衅?
    • 不想挑衅。在我看来,OP 设置得非常好,而您似乎将其作为额外的工作搁置一旁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 2019-02-10
    相关资源
    最近更新 更多