2014-01-06 62 views
3

表示通过输入更新状态的最佳方式是什么?Haskell monad用于模拟

我模拟物理系统。它有状态(坐标,速度)。状态通过模拟进行更新,该模拟需要来自stdin的一些参数(力)。结果在每个模拟周期结束后变为stdout

程序应该在N个周期后停止。

我已经完成了readIORefwriteIORef,但这很丑陋。

回答

5

这样做的一个简单方法是沿着一个懒惰的(可能是无限的)列表,而不是做任何明确的IO。

import Control.Monad.State 

-- Prerequisites: 

data SimState  -- coordinates & velocities. 
data SimTVParams -- what's read from input. `instance Read`. 

initialState :: SimState 
simStep :: SimTVParams -> SimState -> SimState 
simStateInfo :: SimState -> String 

-- How to do the simulation: 

main :: IO() 
main = interact $ 
      unlines . map simStateInfo 
     . simulate initialState 
     . map read . lines 

simulate :: SimState -> [SimTVParams] -> [SimState] 
simulate iState = (`evalState` iState) . mapM (state . step) 
where step params oldState = (newState, newState) 
     where newState = simStep params oldState 
+0

谢谢。 N步后它不会退出。但是我可以通过stdin通过stdin-n N来做到这一点。 –

+0

没错。停止'eof'而不是固定数量的步骤对于这样的任务来说更自然。 – leftaroundabout

2

您可以使用pipes以非常高的级别编写此代码。首先,我会假设你已经定义了以下几种类型,价值和功能:

data Status = Status deriving (Show) 
data Param = Param deriving (Read) 

initialState :: Status 
initialState = undefined 

update :: Status -> Param -> Status 
update = undefined 

numCycles :: Int 
numCycles = undefined 

现在,这里是管道代码:

import Pipes 
import qualified Pipes.Prelude as P 

main = runEffect $ 
    P.readLn >-> P.take numCycles >-> P.scan update initialState id >-> P.print 

你可以看这是从数据处理管道从左到右:

  • P.readLn从标准输入读取输入参数。如果达到输入
  • P.take numCycles的端会停止只允许多达numCycles参数通过
  • P.scan使用所提供的初始状态和更新函数运行你的模拟,然后流更下游
  • P.print打印每一个中间状态出每个中间状态

要了解关于pipes库的更多信息,可以阅读official pipes tutorial