Haskell模拟中的Monad

3

如何最好地表示由输入更新的状态?

我模拟物理系统。它有状态(坐标,速度)。 状态通过从 stdin 中获取一些参数(力)进行模拟更新。每次模拟周期后结果都会被输出到 stdout

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

我已经使用了 readIORefwriteIORef 但这很丑陋。

2个回答

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

谢谢。它在N步后不会退出。但是我可以通过在sh中传递stdin -n N来做到这一点。 - Aleksandr Pakhomov
没错。对于这种任务,停在eof而不是固定步数上更加自然。 - leftaroundabout

2
你可以使用“管道”在非常高的级别上编写这个。首先,我假设您已经定义了以下类型、值和函数:
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 库的信息,你可以阅读 官方 pipes 教程

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接