STArray、STUArray、IOArray、IOUArray の使い方を載せます。コピー&ペーストですぐに動かせるように、インポート文を併記しています。インポートされているモジュール Data.Array.IO、Data.Array.ST は、arrayパッケージに含まれています。
import Control.Monad.ST
import Data.Array.ST
makeSTArray :: ST s (STArray s Int Bool)
makeSTArray = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む
import Control.Monad.ST
import Data.Array.ST
makeSTUArray :: ST s (STUArray s Int Bool)
makeSTUArray = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む
import Data.Array.IO
makeIOArray :: IO (IOArray Int Bool)
makeIOArray = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む
import Data.Array.IO
makeIOUArray :: IO (IOUArray Int Bool)
makeIOUArray = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む
それぞれの関数を比較してみると、「配列を生成し、50番目の要素のBool値を反転させた値を55番目の要素に書き込む」という操作に、コード上の差異がないことがわかります。
make = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む
これは、4つの配列がMArray型クラスのインスタンスだからです。MArray型クラスは、配列を操作するための関数newArray、readArray、writeArrayの実装をインスタンスに要求します。実装は強制ではありませんが、4つの配列はこれらの関数を実装しています。
4つの配列すべてがMArray型クラスのインスタンスであるため、操作手順が同じであれば、配列の種類が異なっていても共通化することができます。
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.ST
import Data.Array.ST
import Data.Array.IO
import Data.Array.MArray
makeSTArray :: ST s (STArray s Int Bool)
makeSTArray = make
makeSTUArray :: ST s (STUArray s Int Bool)
makeSTUArray = make
makeIOArray :: IO (IOArray Int Bool)
makeIOArray = make
makeIOUArray :: IO (IOUArray Int Bool)
makeIOUArray = make
make :: MArray a Bool m => m (a Int Bool)
make = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む
蛇足
make関数の定義は以下のようになっています。
make :: MArray a Bool m => m (a Int Bool)
mはMonad型クラスのインスタンスです。つまり、make関数が返す配列は、何らかのモナドで包まれています。それぞれの配列がどのモナドで包まれているかは、MArrayのインスタンス定義を見ることで分かります。
class Monad m => MArray a e m where
instance MArray (STArray s) e (ST s) where
instance MArray (STUArray s) Bool (ST s) where
instance MArray IOArray e IO where
instance MArray IOUArray Bool IO where
上記定義から、STArrayとSTUArrayはST sモナドに包まれ、IOArrayとIOUArrayはIOモナドに包まれていることがわかります。
IOモナドに包まれた配列を純粋な世界に脱出させることはできませんが、STモナドに包まれた配列を取り出すことはできます。取り出すには、runSTArrayもしくはrunSTUArray関数を使います。
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.ST
import Data.Array
import Data.Array.Unboxed
import Data.Array.ST
import Data.Array.MArray
takeSTArray :: Array Int Bool
takeSTArray = runSTArray makeSTArray
takeSTUArray :: UArray Int Bool
takeSTUArray = runSTUArray makeSTUArray
makeSTArray :: ST s (STArray s Int Bool)
makeSTArray = make
makeSTUArray :: ST s (STUArray s Int Bool)
makeSTUArray = make
make :: MArray a Bool m => m (a Int Bool)
make = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む
取り出した配列は、MArray型クラスのインスタンスではなく、IArrayのインスタンスになっています。これは、モナドから脱出したことで破壊的操作ができなくなったことを意味します。
上記コードは、以下のように簡略化できます。
{-# LANGUAGE FlexibleContexts #-}
import Data.Array
import Data.Array.Unboxed
import Data.Array.ST
import Data.Array.MArray
takeSTArray :: Array Int Bool
takeSTArray = runSTArray make
takeSTUArray :: UArray Int Bool
takeSTUArray = runSTUArray make
make :: MArray a Bool m => m (a Int Bool)
make = do
arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む