2014-01-06 89 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