haskell - 如何使用适当的 IO monad 打印无限列表中的每个项目
问题描述
我有一个简单的整数定义,用一个无限列表完成:
nats = 0: map (+1) nats
我想制作一个打印每个元素的无限列表的版本。
我做到了Data.Trace (trace)
nats = 0: map (\x -> 1 + trace (show x) x) nats
但这有点作弊。使用 IO monad 正确执行此操作会更好。
IO [Int]
我尝试使用以下代码使用无限类型的列表来做到这一点:
nats' = (fmap f nats') >>= mapM (\x -> do{print x; return x})
where f l = 0:map (+1) l
类型签名有效,但是当我尝试使用 评估它时take 10 'nats
,它会陷入无限递归而没有打印任何内容。
这段代码有什么问题,类型签名是否正确,解决这个问题的正确方法是什么?
编辑 :我有这个问题的一个变体,我想创建一个包含用户所有输入的无限列表。我怎样才能做到这一点?
解决方案
IO monad 是严格的。当我们运行时,我们在绑定到一个值之前x <- ioAction
完全执行ioAction
并观察它的所有副作用。x
因此, ifioAction
是一个无限循环,它永远不会返回要绑定到的值x
。
在您的情况下,代码具有以下形式
nats' = (fmap f nats') >>= ...
这相当于
nats' = nats' >>= return . f >>= ...
因此,nats'
将在执行之前递归return . f
,导致无限循环并阻止任何输出。
如何使它真正起作用?
最惯用的 Haskell 方法是将副作用与实际计算分开:我们定义一个纯值
nats = 0 : map (+1) nats
我们打印出来
nats' = for_ nats print
请注意,上面使用for
/ traverse
/ mapM
(而不是它们的_
变体)几乎没有意义,因为列表是无限的,因此它永远不会返回值,而只会执行无限多次打印。
如果您真的想交错 IO 和计算,即使这通常不那么惯用,您也可以使用递归:
nats' = go 0
where go l = print l >>= go (l+1)
如果你想尝试返回一个列表,即使这实际上永远不会发生,你可以使用这个:
nats' = go 0
where go l = do
print l
ls <- go (l+1)
return (l:ls)
然而,这是不必要的复杂。
推荐阅读
- python - OpenCV函数计算与图像中心形成的像素的角度
- javascript - 为什么此用户脚本不适用于我的网站
- node.js - 限制 Node JS express 中的 api 访问
- python - 不断增加函数中变量的值,我越调用它
- python - 如何处理 tensorflow v2 中的变量未初始化错误
- java - Maven 构建问题:安装了外部 Maven 3.6 但插件 2.5 出现构建错误
- c++ - 为什么tellp() 给出-1?
- graphql - prisma 部署后,prisma 没有应用更改
- c++ - 在没有同步的情况下松弛的原子和内存一致性
- sql - 您如何找到前 100 名客户的变化百分比?