使用 >>= 和 =<< 运算符在 Haskell 中组合 IO
Using the >>= and =<< operators to combine IO in Haskell
我正在尝试运行“无限”模拟,打印每一步的结果。
有一个函数 nextFrameR
接受输入 Map
并将模拟向前推进到 return 输出 Map
,然后有一个 render_
接受输入 Map
并将一些东西打印到 stdout
、return 输入 Map
的函数(以便我可以使用 iterate
或类似的东西) .
作为 Haskell 的新手,我真的很难将所有这些片段组合在一起。我发现 this answer 非常有趣,但由于这两个功能的结合,我不确定如何将其直接付诸实践(我尝试使用 liftM2
和 iterate
)。
类型签名如下:
nextFrameR :: Map -> IO Map
render_ :: Map -> IO Map -- originally Map -> IO ()
我不太确定从这里到哪里去,我可以做类似的事情:
(iterate (>>= nextFrameR) initialMap) :: [IO Map]
但这只是给了我一个(无限的?)帧列表(我认为),这很好,它只是不允许我打印它们,因为我不知道如何在其中组合渲染功能.
iterate
对于非 IO 计算工作得很好,但是如果你在 IO
中,你不能轻易利用 iterate
.
看看为什么,你的列表
(iterate (>>= nextFrameR) initialMap) :: [IO Map]
是
[ initialMap
, initialMap >>= nextFrameR
, initialMap >>= nextFrameR >>= nextFrameR
...
所以...我们如何利用它来实现无限循环?我们不能拿不存在的“最后一个元素”。我们也不能按顺序执行该列表中的所有操作,因为那样会 运行 initialMap
很多次。
如果我们避免使用 iterate
并求助于递归等基础知识会更容易:
loop :: Map -> IO ()
loop m = do
m' <- nextFrameR m
render_ m' -- it looks like you want this
-- feel free to add some delay here, or some stopping condition to exit the loop
loop m'
main :: IO ()
main = do
m <- initialMap
loop m
你可以把上面的代码变成一些使用>>=
的代码,但没有必要。
最后,没必要让render_
return一样Map
。你可以做到 return IO ()
.
如果您是初学者,我建议您最初远离 mapM_, traverse, for, sequence, liftM2, ap, ...
等“智能”库函数,并学习仅使用 do
块和递归来完成所有操作。然后,一旦您了解了它的工作原理,您就可以尝试利用库助手改进您的代码。
我正在尝试运行“无限”模拟,打印每一步的结果。
有一个函数 nextFrameR
接受输入 Map
并将模拟向前推进到 return 输出 Map
,然后有一个 render_
接受输入 Map
并将一些东西打印到 stdout
、return 输入 Map
的函数(以便我可以使用 iterate
或类似的东西) .
作为 Haskell 的新手,我真的很难将所有这些片段组合在一起。我发现 this answer 非常有趣,但由于这两个功能的结合,我不确定如何将其直接付诸实践(我尝试使用 liftM2
和 iterate
)。
类型签名如下:
nextFrameR :: Map -> IO Map
render_ :: Map -> IO Map -- originally Map -> IO ()
我不太确定从这里到哪里去,我可以做类似的事情:
(iterate (>>= nextFrameR) initialMap) :: [IO Map]
但这只是给了我一个(无限的?)帧列表(我认为),这很好,它只是不允许我打印它们,因为我不知道如何在其中组合渲染功能.
iterate
对于非 IO 计算工作得很好,但是如果你在 IO
中,你不能轻易利用 iterate
.
看看为什么,你的列表
(iterate (>>= nextFrameR) initialMap) :: [IO Map]
是
[ initialMap
, initialMap >>= nextFrameR
, initialMap >>= nextFrameR >>= nextFrameR
...
所以...我们如何利用它来实现无限循环?我们不能拿不存在的“最后一个元素”。我们也不能按顺序执行该列表中的所有操作,因为那样会 运行 initialMap
很多次。
如果我们避免使用 iterate
并求助于递归等基础知识会更容易:
loop :: Map -> IO ()
loop m = do
m' <- nextFrameR m
render_ m' -- it looks like you want this
-- feel free to add some delay here, or some stopping condition to exit the loop
loop m'
main :: IO ()
main = do
m <- initialMap
loop m
你可以把上面的代码变成一些使用>>=
的代码,但没有必要。
最后,没必要让render_
return一样Map
。你可以做到 return IO ()
.
如果您是初学者,我建议您最初远离 mapM_, traverse, for, sequence, liftM2, ap, ...
等“智能”库函数,并学习仅使用 do
块和递归来完成所有操作。然后,一旦您了解了它的工作原理,您就可以尝试利用库助手改进您的代码。