昨天我終於決定開始學習Haskell了。我開始通過教程進行掃描,但很快決定實際練習會更有益。因此,我開始將我的python腳本移植到Haskell中。令我驚訝的是,它實際上工作和生成的值匹配的蟒蛇。用盡Haskell的內存回憶
我意識到實施可能是絕對可怕的。可怕的性能不足並不會讓我感到困擾,但讓我困擾的是,當我試圖長時間運行模擬時,我一直在用盡內存。這是因爲執行本身是有缺陷的,還是可以使其工作?我試圖用三種不同的方法構建主循環:「迭代」,一個遞歸函數(我讀了關於尾遞歸,但沒有設法使它工作)和一個更實驗性的遞歸函數。有問題的功能被命名爲模擬,測試和TEST2。我用選項「-O2」編譯程序。
爲什麼程序內存不足,我該怎麼做才能避免這種情況?
代碼不那麼重要的部分:
import System.Environment
import Data.List (tails)
import System.CPUTime
import Text.Printf
import Control.Exception
gConst = 6.674e-11
data Vector = Vector2D Double Double | Vector3D Double Double Double deriving (Show)
deltaVector :: Vector -> Vector -> Vector
deltaVector (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x2 - x1) (y2 - y1)
deltaVector (Vector3D x1 y1 z1) (Vector3D x2 y2 z2) = Vector3D (x2 - x1) (y2 - y1) (z2 - z1)
data Position = Position Vector deriving (Show)
data Velocity = Velocity Vector deriving (Show)
distance2DSquared (Vector2D deltaX deltaY) = deltaX ** 2 + deltaY ** 2
distance3DSquared (Vector3D deltaX deltaY deltaZ) = (distance2DSquared $ Vector2D deltaX deltaY) + deltaZ ** 2
distance vector = sqrt (distance3DSquared $ vector)
force vector mass1 mass2 = gConst * (mass1 * mass2)/(distance3DSquared vector)
acceleration force mass = force/mass
vectorComponentDivide (Vector2D x y) c = Vector2D (x/c) (y/c)
vectorComponentDivide (Vector3D x y z) c = Vector3D (x/c) (y/c) (z/c)
vectorComponentMultiply (Vector2D x y) c = Vector2D (x*c) (y*c)
vectorComponentMultiply (Vector3D x y z) c = Vector3D (x*c) (y*c) (z*c)
vectorComponentAdd (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1+x2) (y1+y2)
vectorComponentAdd (Vector3D x1 y1 z1) (Vector3D x2 y2 z2) = Vector3D (x1+x2) (y1+y2) (z1+z2)
invertedVector (Vector2D x1 y1) = Vector2D (-x1) (-y1)
invertedVector (Vector3D x1 y1 z1) = Vector3D (-x1) (-y1) (-z1)
normalizedVector :: Vector -> Vector
normalizedVector vector = vectorComponentDivide vector $ distance vector
velocity vel0 mass1 mass2 vector deltaT =
vectorComponentMultiply (vectorComponentAdd vel0 (vectorComponentMultiply (normalizedVector vector) (acceleration (force vector mass1 mass2) mass1))) deltaT
data Actor = Actor String Vector Vector Double deriving (Show)
earth = Actor "Object1" (Vector3D 0 0 0) (Vector3D 0 0 0) 10
moon = Actor "Object2" (Vector3D 10 0 0) (Vector3D 0 0 0) 10
actors = [earth, moon]
combinations :: Int -> [a] -> [[a]]
combinations 0 _ = [ [] ]
combinations n xs = [ y:ys | y:xs' <- tails xs
, ys <- combinations (n-1) xs']
updateVelocity [(Actor name1 position1 velocity1 mass1),(Actor name2 position2 velocity2 mass2)] =
[(Actor name1 position1 a mass1),(Actor name2 position2 b mass2)]
where a = velocity velocity1 mass1 mass2 vector deltaT
b = velocity velocity2 mass2 mass1 (invertedVector vector) deltaT
vector = deltaVector position1 position2
deltaT = 1
updatePosition [(Actor name1 position1 velocity1 mass1),(Actor name2 position2 velocity2 mass2)] =
[Actor name1 (vectorComponentAdd position1 velocity1) velocity1 mass1, Actor name2 (vectorComponentAdd position2 velocity2) velocity2 mass2]
相關部分:
update list = map updatePosition (map updateVelocity list)
simulation state n = do
if n == 0
then do
print state
return()
else do
let newState = update state
simulation newState $! (n-1)
test list n = iterate update list !! n
test2 list 0 = list
test2 list n = (test2 (update list) (n-1))
time :: IO t -> IO t
time a = do
start <- getCPUTime
v <- a
end <- getCPUTime
let diff = (fromIntegral (end - start))/(10^12)
printf "Computation time: %0.3f sec\n" (diff :: Double)
return v
main :: IO()
main = do
let combo = combinations 2 actors
putStrLn "Hello World!"
let n = 1000000
print n
--time $ print (test combo n)
time $ simulation combo n
_ <- getLine
putStrLn "BAI"
我做了[某些文體等改進/備註](https://gist.github.com/leftaroundabout/35fcbed5e51b5a4535d2)。 (沒有做的懶惰內存問題,雖然)。 – leftaroundabout
@leftaroundabout非常感謝!我一定會研究你提供的評論。矢量操作的指定方式尤其有趣。絕對比我使用的基於純函數的方法更優雅。 – Jaaisto