Haskell不是懒惰的评估交错

时间:2014-08-29 03:04:17

标签: haskell

我正在解决一个问题(它来自UPenn类,但我没有接受它(只是通过它来学习Haskell)),重点是构造一个Stream(定义如下),定义由"统治者" (标尺!! n = 2的最高幂的指数,它将除以n)。问题在于我认为下面标尺的定义应该懒惰地评估,但它似乎是严格评估并且无限运行。如果我通过添加类似" nthStream 10 = streamRepeat 10"的终端案例来限制它。它运行并生成输出到我想要的点。

data Stream a = Stream a (Stream a)

streamToList :: Stream a -> [a]
streamToList (Stream a rest) = [a] ++ streamToList rest

instance Show a => Show (Stream a) where
    show s = show $ take 100 $ streamToList s

streamRepeat :: a -> Stream a
streamRepeat a = Stream a (streamRepeat a)

interleaveStreams :: Stream a -> Stream a -> Stream a
interleaveStreams (Stream a arest) (Stream b brest) = (Stream a (Stream b (interleaveStreams arest brest)))

nthStream :: Integer -> Stream Integer

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

ruler :: Stream Integer
ruler = nthStream 0 

任何人都可以解释为什么统治者(和nthStream)没有懒惰地评估?

2 个答案:

答案 0 :(得分:5)

我会试着把你推向正确的方向,或者至少指出出了什么问题。函数nthStream永远不会评估,甚至不会评估它的第一个元素,因为它是用interleaveStreams定义的。为了给你一个例子,让我们弄清楚它为nthStream 0评估的内容(为了简洁和可读性,我将nthStream重命名为nthinterleaveStream改为interleave },streamRepeatrepeatStreamStrm):

nth 0 = interleave (repeat 0) (nth 1)
      = interleave (Strm 0 _0) (interleave (repeat 1) (nth 2))
      = interleave (Strm 0 _0) (interleave (Strm 1 _1) (nth 2))
      = interleave (Strm 0 _0) (interleave (Strm 1 _1) (interleave (repeat 2) (nth 3)))
      = interleave (Strm 0 _0) (interleave (Strm 1 _1) (interleave (Strm 2 _2) (nth 3)))

我选择将repeat返回的每个流的尾部表示为_N,其中N是重复的数字。这些目前是thunk,因为我们还没有要求他们的价值观。请注意正在构建的是interleave s链,而不是Strm构造函数链。我们得到了我们感兴趣的每个人,但他们永远不会从interleave返回,直到评估第二个参数。由于第二个参数总是减少为对interleave的新调用,因此它永远不会减少。

这是一个提示:如何递归定义{{​​1}},以便它只取决于它已经构造的第一个参数?即。

interleaveStreams

答案 1 :(得分:5)

你的问题就在这一行

interleaveStreams (Stream a arest) (Stream b brest) = (Stream a (Stream b (interleaveStreams arest brest)))

为了使该函数甚至返回结果的开头,必须评估其参数的两个,因为您直接在其构造函数上进行模式匹配。但是你在

中使用它
nthStream n = interleaveStreams (streamRepeat n) (nthStream (n+1))

这意味着nthStream n在评估nthStream (n+1)之前无法返回任何,这会为您提供无限递归。

要解决此问题,您可以使用~将问题行中的第二个模式更改为显式 lazy

interleaveStreams (Stream a arest) ~(Stream b brest) = (Stream a (Stream b (interleaveStreams arest brest)))