List 使用>>;=和=<&书信电报;在Haskell中组合IO的运算符
我正在尝试运行“无限”模拟,打印每个步骤的结果 有一个函数List 使用>>;=和=<&书信电报;在Haskell中组合IO的运算符,list,haskell,monads,lazy-evaluation,lazy-sequences,List,Haskell,Monads,Lazy Evaluation,Lazy Sequences,我正在尝试运行“无限”模拟,打印每个步骤的结果 有一个函数nextframe,它接受一个输入Map,并向前推进模拟以返回一个输出Map,然后有一个render\uu函数,它接受一个输入Map,并将一些东西打印到stdout,返回输入Map(这样我就可以使用迭代或类似的方法) 由于我对Haskell比较陌生,所以我真的很难将所有这些部分放在一起。我发现非常有趣,但由于这两个函数的组合,我不确定如何直接将其付诸实践(我尝试过使用liftM2和iterate) 类型签名如下所示: nextframe:
nextframe
,它接受一个输入Map
,并向前推进模拟以返回一个输出Map
,然后有一个render\uu
函数,它接受一个输入Map
,并将一些东西打印到stdout
,返回输入Map
(这样我就可以使用迭代或类似的方法)
由于我对Haskell比较陌生,所以我真的很难将所有这些部分放在一起。我发现非常有趣,但由于这两个函数的组合,我不确定如何直接将其付诸实践(我尝试过使用liftM2
和iterate
)
类型签名如下所示:
nextframe::Map->IO映射
渲染:映射->IO映射--原始映射->IO()
我真的不知道该从这里走到哪里,我可以这样做:
(迭代(>>=nextframe)初始映射)::[IO映射]
但这只是给了我一个(无限?)帧列表(我认为),这很好,它不允许我打印它们,因为我不知道如何在其中组合渲染功能。iterate
对于非IO计算非常有效,但是如果你在IO
中,你就不能轻易地利用iterate
要了解原因,请列出您的列表
(iterate (>>= nextFrameR) initialMap) :: [IO Map]
是
那么…我们怎么能利用它来产生一个无限循环呢?我们不能接受不存在的“最后一个元素”。我们也不能按顺序执行该列表中的所有操作,因为这会运行多次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
loop::Map->IO()
循环m=do
m'=
但是没有必要这样做
最后,无需使render\uuu
返回相同的Map
。您可以使该返回IO()
如果你是初学者,我建议你一开始不要使用“智能”像mapM\u、遍历、for、sequence、liftM2、ap、
这样的库函数,并学会只使用do
-块和递归来做任何事情。然后,一旦你了解了它的工作原理,你就可以试着利用库帮助来改进你的代码。表达式sequence(iterate(>=nextframe)initialMap)
将具有IO[Map]
类型。但是,与在早期阶段涉及IO不同,您可以使用:frames=iterate nextFrameR initialMap
,编写一个类型为Map->String
的普通渲染函数。然后使用Map render frames
查看文本跟踪输出。至于是否使用IO的决定应该留给最高层el调用代码。问题是nextFrameR
在每次运行时都会创建一个newStdGen
。我有一个版本的代码,它接受RandomGen
(这就是nextFrameR
在引擎盖下调用的内容)但我不在顶层。我明白了,但在每一步创建一个新的生成器有点不寻常。你可以在步骤N中传递随机数生成器的最终状态,使之成为步骤N+1中生成器的初始状态。这样你就可以得到统计保证,这只在单个随机序列中可用。如果你想的话为了能够使用相同的随机数但不同的物理参数运行稍后的模拟,newStdGen使这不可能,因为您无法控制种子。@jpmarinier这就是为什么我有一个版本的函数采用RandomGen
()。我认为这解决了您正在谈论的问题,但可能没有?我确实希望能够获得可复制的测试结果。不完全是这样。我在您的Github页面中看到,您的函数具有以下类型签名:nextFrame::RandomGen=>gen->Map->Map
。因此,它不会保留生成器的最终状态以备将来使用访问。您可能需要此签名:nextFrame::RandomGen=>gen->Map->(Map,gen)
。但是我看到库shuffle'
函数会放弃最终状态。在其源代码中看到对fst
的调用,并且没有对snd
的调用。因此,您必须调整shuffle'
源代码,或者安排使用shuffleM
与runRand
一起使用。我解决这一问题的本能会很好我一直在用一个do
块来解决这个问题,但是在用这个块编写了一些代码,然后重构为使用>>=等之后,我也在尝试同样的方法。你是对的,我可能应该先尝试一个更简单的版本,我现在就试试看它是否有效。这很有效,但是根据最初的问题,有没有一个好的方法来使用v各种各样的操作员将代码转换成“更智能”的东西或者不是真的?感觉就像这些单子和各种操作符就是为这种事情而设计的。@GTF这是一个品味的问题。简单的do
代码很短,任何人都可以阅读。我无法立即看到如何仅使用常用的助手来实现这一点。当然,有太多的非常用的助手,例如,哪个可以这似乎正是您所需要的。这应该仍然可以,但在使用高级帮助程序时,请始终检查代码是否仍然可读。当代码不可读时,编写“聪明”代码是没有意义的。“基本”不包括适当的流式API可能会让新手感到困惑。人们学习了那些在纯上下文中完美工作的漂亮列表处理函数,但一旦在某个地方添加了效果,就会发现mapM
和朋友们并没有按需要“流式处理”。然后,人们就只能编写更多的“整体式”一元块在一定程度上耦合了生成和处理,给人的印象是这些高级函数是为纯世界保留的。@GTF如果您真的想使用现有的运算符,您可以编写
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