Haskell

【Haskell】様々な配列その2(MArray編)

More than 1 year has passed since last update.

STArraySTUArrayIOArrayIOUArray の使い方を載せます。コピー&ペーストですぐに動かせるように、インポート文を併記しています。インポートされているモジュール Data.Array.IOData.Array.ST は、arrayパッケージに含まれています。


STArray

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 -- 配列をモナドに包む



STUArray

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 -- 配列をモナドに包む



IOArray

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 -- 配列をモナドに包む



IOUArray

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番目の要素に書き込む」という操作に、コード上の差異がないことがわかります。


4つの関数の定義

make = do

arr <- newArray (1,100) True -- 配列の作成
val <- readArray arr 50 -- 要素の値を取得
writeArray arr 55 (not val) -- 要素の値を変更
return arr -- 配列をモナドに包む

これは、4つの配列がMArray型クラスのインスタンスだからです。MArray型クラスは、配列を操作するための関数newArrayreadArraywriteArrayの実装をインスタンスに要求します。実装は強制ではありませんが、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)

mMonad型クラスのインスタンスです。つまり、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

上記定義から、STArraySTUArrayST sモナドに包まれ、IOArrayIOUArrayIOモナドに包まれていることがわかります。

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 -- 配列をモナドに包む