我在Haskell中为编程竞赛实现基于粒子的流体模拟时遇到了一些问题。我目前有一系列粒子在每个模拟步骤中被修改。每个粒子是2个向量的元组:位置和速度(我自己的Vec3D模块)。在某些时候,我需要从粒子中提取位置(有点像解压缩列表),我试图这样做:
let xs = runSTArray $ do
xs' <- newArray (min, max) (0.0,0.0,0.0) :: ST s (STArray s Int Vec3D)
forM_ [min..max] $ \j -> do
(x, v) <- readArray ps' j
writeArray xs' j x
return xs'
let displacements = doubleDensityRelaxation xs restDen k kNear t h
其中ps'
和doubleDensityRelaxation
的类型为
type Vec3D = (Double, Double, Double)
ps' :: ST s (STArray s Int (Vec3D, Vec3D))
doubleDensityRelaxation :: Array Int Vec3D -> Double -> Double -> Double -> Double -> Double -> Array Int Vec3D
因此xs
应为xs :: Array Int Vec3D
类型。但是,我得到了
Simulator.hs:76:35:
No instance for (MArray (STArray s) (Vec3D, Vec3D) (ST s1))
arising from a use of `readArray'
Possible fix:
add an instance declaration for
(MArray (STArray s) (Vec3D, Vec3D) (ST s1))
In a stmt of a 'do' block: (x, v) <- readArray ps' j
In the expression:
do { (x, v) <- readArray ps' j;
writeArray xs' j x }
In the second argument of `($)', namely
`\ j
-> do { (x, v) <- readArray ps' j;
writeArray xs' j x }'
来自编译器我不明白,因为readArray
不应该返回整个数组;只有一个(Vec3D, Vec3D)
元素。
作为修复,我可以让doubleDensityRelaxation
直接接受ST s (STArray s Int Vec3D)
吗?
如果我更改类似的类型并删除let xs = runSTArray $ do
部分,我会
Couldn't match expected type `ST s0 (STArray s0 Int Vec3D)'
with actual type `STArray s Int Vec3D'
但如果我将(ST s xs')
作为输入而不仅仅是xs'
,则会抱怨数据构造函数ST
不在范围内。我的进口目前是
import Data.List
import Data.Array
import Data.Array.ST
import Control.Monad
import Control.Monad.ST
import Vec3D
完成功能:
step :: Array Int (Vec3D, Vec3D) -> Vec3D -> Double -> Double -> Double -> Double -> Double -> Array Int (Vec3D, Vec3D)
step ps g restDen k kNear t h = runSTArray $ do
ps' <- thaw ps :: ST s (STArray s Int Particle)
--GRAVITY
forM_ [min..max] $ \i -> do
(x, v) <- readArray ps' i
writeArray ps' i (x, addGravity v g t)
--TODO - VISCOSITY
--MOVE
xsOld <- newArray (min, max) (0.0,0.0,0.0) :: ST s(STArray s Int Vec3D)
forM_ [min..max] $ \i -> do
(x, v) <- readArray ps' i
writeArray xsOld i x
writeArray ps' i (x `add` (v `mulSc` t), v)
--TODO - SPRINGS
--DOUBLE DENSITY RELAXATION
xs' <- newArray (min, max) (0.0,0.0,0.0) :: ST s (STArray s Int Vec3D)
forM_ [min..max] $ \j -> do
(x, v) <- readArray ps' j
writeArray xs' j x
let displacements = doubleDensityRelaxation (freeze xs') restDen k kNear t h
ps <- newArray (min, max) ((0.0,0.0,0.0), (0.0,0.0,0.0)) :: ST s (STArray s Int (Vec3D, Vec3D))
--TODO incomplete
return ps
where
addGravity v g t = v `add` (g `mulSc` t)
(min, max) = bounds ps
答案 0 :(得分:0)
社区维基回答基于评论:
如果您将freeze
来自let displacements = ...
行的呼叫移至xs'' <- freeze xs'
,然后使用xs''
代替freeze xs'
这是因为freeze :: (Ix i, MArray a e m, IArray b e) => a i e -> m (b i e)
。因此freeze xs'
是IArray b Vec3D => ST s (b Int Vec3D)
。要获取实际数组,您需要在freeze
内运行runSTArray
。