Haskell 需要帮助理解流
Posted
技术标签:
【中文标题】Haskell 需要帮助理解流【英文标题】:Haskell need help understanding stream 【发布时间】:2022-01-15 21:29:03 【问题描述】:我有这些功能的代码:
-
从给定的流中创建一个无限列表;
为给定的迭代函数和起始流元素(种子)创建一个流;
将两个流合并为一个,以便它们的元素交错。
代码如下:
data Stream a = Cons a (Stream a)
streamToList :: Stream a -> [a]
streamToList (Cons x xs) = x : streamToList xs
streamIterate :: (a -> a) -> a -> Stream a
streamIterate f x = Cons x (streamIterate f (f x))
streamIterLeave :: Stream a -> Stream a -> Stream a
streamIterLeave (Cons x xs) ys = Cons x (streamIterLeave ys xs)
现在我遇到的问题是我不知道如何在 ghci 上检查此代码。具体来说,如何在命令行中输入流?
例如我试过streamToList [1,2,3]
; streamToList (1,2,3)
; streamToList 1,2,3
并且它们似乎都没有工作。
非常感谢您的帮助!
【问题讨论】:
你不能构造一个有限的流,因为你没有空的情况。因此,您不能像Cons 'a' (Cons 'b' (...))
那样手动构建一个。但是,您可以依赖无限递归:foo i = Cons i (foo $ i + 1)
。
你可以试试s1 = streamIterate (+2) 0
,s2 = streamIterate (+2) 1
,然后take 20 $ streamToList s1
,同样的s2
,甚至take20 $ streamToList $ streamInterleave s1 s2
。
请注意,您的任何尝试都不会构造 Stream
值;您分别有列表、元组和语法错误。构造Stream
值的唯一方法是使用Cons
,因此(co)recuson 是必要的。
@IvenMarquardt 人们往往会忘记undefined
。 :) take 2 $ streamToList $ Cons 1 $ Cons 2 undefined
有效。
【参考方案1】:
几种方法。最简单的就是使用undefined
:
> take 2 $ streamToList $ Cons 1 $ Cons 2 undefined
[1,2]
您可以在提示符下定义命名流,
> let one = Cons 1 two ; two = Cons 2 one
或多行输入,
> :
| one = Cons 1 two
| two = Cons 2 one
| :
> take 5 $ streamToList one
[1,2,1,2,1]
您可以使用您的 streamIterate
(如 cmets 所示)或使用 foldr
从列表中创建您的流,
> take 5 $ streamToList $ streamIterate (+1) 0
[0,1,2,3,4]
> listToStream xs = foldr Cons undefined xs
> intS = listToStream [1..]
> take 5 $ streamToList intS
[1,2,3,4,5]
定义了listToStream
,您可以进一步轻松地定义
> mapStream f s = listToStream . map f . streamToList $ s
> intsStream = x where x = Cons 1 $ mapStream (1+) x
> take 5 $ streamToList intsStream
[1,2,3,4,5]
> zipStr s1 s2 = listToStream $ zip (streamToList s1) (streamToList s2)
> zipStrWith f s1 s2 = mapStream (uncurry f) (zipStr s1 s2)
> addStreams s1 s2 = zipStrWith (+) s1 s2
> fibS = let a = Cons 0 b; b = Cons 1 c; c = addStreams a b in a
> take 15 $ streamToList fibS
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]
等等。等等等等。
正如dfeuer 在 cmets 中提到的那样,上面的listToStream
有点虚假——它允许无效(即有限)列表作为其参数。在 REPL 上玩它是可以的,但不是一般的。
通过定义“将虚假信息拉到呼叫站点”可能会更好
> :
| (+++) :: [a] -> Stream a -> Stream a
| xs +++ ys = foldr Cons ys xs
| :
允许我们定义流
> good = [1..] +++ undefined
> bad = [1..10] +++ undefined
如果存在问题,在哪里会更加明显。
【讨论】:
listToStream
有点假。也许使用完全合理的(+++) :: [a] -> Stream a -> Stream a; xs +++ ys = foldr Cons ys xs
将虚假信息拉到呼叫站点会更好。
@dfeuer 有趣的想法,谢谢。以上是关于Haskell 需要帮助理解流的主要内容,如果未能解决你的问题,请参考以下文章