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) 0s2 = 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 需要帮助理解流的主要内容,如果未能解决你的问题,请参考以下文章

来自元组的函数 - Haskell

Haskell 中的非确定性是啥?

haskell中的查找表加速

在 Haskell 中整理列表理解

EMQ Sharing Day 003|Haskell 语言的配置和基本概念

haskell列表理解通过列表monad理解