我正在尝试运行一个;"无限";模拟,打印每个步骤的结果。
有一个函数nextFrameR
,它接受一个输入Map
,并推进模拟以返回一个输出Map
,还有一个render_
函数,它接受输入Map
,并将一些东西打印到stdout
,返回输入Map
(这样我就可以使用iterate
或类似的东西(。
我真的很难把所有这些东西组合在一起,因为我对哈斯克尔来说相对陌生。我发现这个答案非常有趣,但由于这两个功能的结合,我不确定如何将其直接付诸实践(我曾尝试过使用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_
返回相同的Map
。您可以使该返回IO ()
。
如果你是一个初学者,我建议你一开始就远离";"聪明";像mapM_, traverse, for, sequence, liftM2, ap, ...
这样的库函数,并学习只使用do
块和递归来完成所有操作。然后,一旦了解了它的工作原理,就可以尝试利用库助手来改进代码。