我正在尝试学习Haskell,所以我决定编写一个简单的程序来模拟太阳周围行星的轨道,但是我遇到了从模拟打印出坐标的问题,我的代码中的顶级函数如下:
runSim :: [Body] -> Integer -> Double -> [Body]
runSim bodys 0 dtparam = bodys
runSim bodys numSteps dtparam = runSim (map (integratePos dtparam . integrateVel dtparam (calculateForce bodys)) (numSteps-1) dtparam
main = do
let planets = runSim [earth, sun] 100 0.05
print planets
"Body"只是一个保持行星位置,速度等的数据类型,因此第一个参数只是模拟中的行星列表,其他参数分别是要积分的步数和时间步长.我的问题是,在每次调用runsim之后,如何修改代码以打印出所有身体的位置?我尝试将"printInfo"函数添加到传递给map的组合函数中,如下所示:
printInfo :: Body -> Body
printInfo b = do
putStrLn b
b
但它没有编译,任何人都可以给我一些提示吗?
谢谢!
yairchu对printBody的问题有一个很好的答案.您的核心问题是,如何构建程序以便打印出每个步骤,这有点困难.大概你想要保持runSim,或类似的东西,纯粹,因为它只是运行模拟,而I/O并不是真正的工作.
有两种方法可以解决这个问题:要么让runSim返回一个无限的模拟步骤列表,要么让I/O包装器一次只运行一步.我更喜欢第一个选项,所以我将从那开始.
更改runSim以返回步骤列表:
runSim :: [Body] -> Double -> [[Body]]
-- now returns a list of bodys, but no terminating condition
runSim bodys numSteps dtparam = nextBodys : runSim nextBodys dtparam
where nextBodys = map (integratePos dtparam . integrateVel dtparam)
(calculateForce bodys)
Run Code Online (Sandbox Code Playgroud)
现在main可以根据需要采取尽可能多的模拟步骤并将其打印出来:
main = mapM_ (mapM_ print) (take 100 $ runSim [earth, sun] 0.05)
Run Code Online (Sandbox Code Playgroud)
再说一次,我会假设,按照yairchu的建议,你可以Body deriving Show这样print做.mapM_就像是map,除了它需要一个monadic(这里,副作用)函数来映射(以M结尾)并且不返回列表(以_结尾).所以真的更像是for-each在Scheme或者其他东西.
另一种方法是保持runSim并编写一个只能一次运行一步的打印循环:
printLoop :: Integer -> [Body] -> IO [Body]
printLoop 0 bodies = return bodies
printLoop n bodies = do
let planets = runSim bodies 1 0.05
mapM_ print planets -- still need to have Body deriving Show
printLoop (n-1) planets
main = do
printLoop 100 [earth, sun]
return ()
Run Code Online (Sandbox Code Playgroud)