Извлечение элементов из STArray (аналогично разархивированию)

У меня небольшая проблема с реализацией симуляции жидкости на основе частиц в 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

1 ответ

Community-Wiki ответ на основе комментариев:

Обновленный код компилируется, если вы переместите 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,

Другие вопросы по тегам