Использование и возврат нескольких STUArrays
Я работал над созданием и использованием нескольких STUArrays в вычислениях ST. Конкретные сценарии:
- создать несколько массивов, но вернуть только один из них
- создать несколько массивов, но не вернуть ни одного из них
- создать несколько массивов и вернуть несколько массивов
У меня есть ответы на (1) и (2), но нет (3).
Сначала немного импорта, чтобы мы знали, откуда все идет:
import Control.Monad.ST (ST,runST)
import Data.Array.Base (unsafeFreezeSTUArray)
import Data.Array.ST (STUArray)
import Data.Array.Unboxed (UArray)
import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef)
import Data.Array.MArray (getBounds, newArray, readArray, writeArray, newListArray)
import Data.Array.ST (runSTUArray)
Для (1) один трюк состоит в том, чтобы определить новый тип данных и функцию конструктора:
data ArrayPair s = AP (STUArray s Int Int) (STUArray s Int Bool)
newAP n = do
a1 <- newArray (1,n) 0
a2 <- newArray (1,n) False
return $ AP a1 a2
А вот как вернуть только один из массивов:
foo :: UArray Int Int
foo = runSTUArray $ do
AP ints bools <- newAP 10
writeArray ints 1 42
writeArray bools 1 True
-- do stuff with ints and bools
return ints
Для (2) вы можете добавить STRef
к структуре данных, завершите вычисление readSTRef
и запустить его с runST
:
data WorkState s = WS (STUArray s Int Int)
(STUArray s Int Bool)
(STRef s Char)
newWS = do
ints <- newArray (1,10) 0
bools <- newArray (1,20) False
char <- newSTRef 'X'
return $ WS ints bools char
bar :: Char
bar = runST $ do
WS ints bools char <- newWS
writeArray ints 3 36
writeArray bools 5 True
writeSTRef char 'Z'
-- ...
readSTRef char
Комментарии к этому подходу для случаев (1) и (2)? А как насчет дела (3)?
baz :: (UArray Int Int, UArray Int Bool)
baz = runST??? $ do
AP ints bools <- newAP
...
return (ints,bools) -- ???
1 ответ
У вас есть 2 варианта:
использование
freeze
, Это требует копирования массивов, но вам не нужно использовать какие-либо небезопасные функции:baz :: (UArray Int Int, UArray Int Bool) baz = runST $ do AP ints bools <- newAP 12 liftM2 (,) (freeze ints) (freeze bools)
Создайте свой вариант
runSTUArray
для двух массивов с помощьюunsafeFreezeSTUArray
зная, что реализация на самом деле безопасна (потому что не останется ссылки на исходные изменяемые массивы).runSTUArray2 :: (Ix i1, Ix i2) => (forall s . (ST s (STUArray s i1 e1, STUArray s i2 e2))) -> (UArray i1 e1, UArray i2 e2) runSTUArray2 st = runST $ do (a1, a2) <- st liftM2 (,) (unsafeFreezeSTUArray a1) (unsafeFreezeSTUArray a2) baz' :: (UArray Int Int, UArray Int Bool) baz' = runSTUArray2 $ do AP ints bools <- newAP 12 return (ints, bools)
(Возможно, этот подход можно даже как-то обобщить, используя Generics, чтобы позволить возвращать любую структуру данных, содержащую
ST
массивы.)