リストを主なデータ構造とする Haskell にも配列はあります。しかし、Web で巡回しても、添字が整数以外でも OK とか、多次元配列の使い方とか、難しい ことを解説してあるページが多く、最も単純な添字が整数の1次元配列の基本的 な使い方に関するページはほとんど無いような気がします。
そこでここでは、高度だけれど難しい機能の解説は他のページに任せて、 思いっきり単純な配列の使い方を解説したいと思います。
Haskell で配列を使うには、まず Data.Array モジュールを import します。 次に配列の生成の仕方ですが、連想リストというデータ構造を準備してやる必要 があります。連想リストとは
[(添字1,要素1),(添字2,要素2),...,(添字n,要素n)]
という形式のデータです。連想リストから配列を作ると、C言語風に書くと
a[添字i] = 要素i (i = 1,2,...,n)
のように配列が初期化されます。連想リストから配列を作るには array 関数を
使います。
array (添字の最小値,添字の最大値) 連想リスト
で配列を生成することができます。具体例を書くと以下のようになります。
array (1,3) [(1,0),(2,1),(3,2)]
配列を表示するときは、この生成方法そのものを使って表します。つまり
ghci で以下のように実行すると、
Prelude Data.Array> let a = array (1,3) [(i,i-1)| i <- [1..3]]
Prelude Data.Array> print a
次のように表示されます。
array (1,3) [(1,0),(2,1),(3,2)]
「Prelude Data.Array>
」は ghci のプロンプトです。
:m + Data.Array
で Data.Array モジュールを読み込んだ状態を
表しています。
生成された配列から要素を読み出すには(!)演算子を使い、「配列!添字」の ように書きます。具体的には以下のようになります。
Prelude Data.Array> let a = array (1,3) [(1,0),(2,1),(3,2)]
Prelude Data.Array> print $ a!2
1
配列を部分的に変更するには(//)演算子を使います。
配列 // 連想リスト
で連想リストにより指定された要素が変更された配列が得られます。このとき、
オリジナルは変更されません。すなわち、オリジナルをコピーして新しい配列を
作ってから変更します。このため、配列の変更には \(O(n)\) の計算量がかかり
ます(\(n\) は要素数)。具体的には以下のようになります。
Prelude Data.Array> let a = array (1,3) [(1,0),(2,1),(3,2)]
Prelude Data.Array> print $ a // [(2,0),(3,0)]
array (1,3) [(1,0),(2,0),(3,0)]
配列をリストに変換するには elems 関数を使います。具体的には 以下のようになります。
Prelude Data.Array> let a = array (1,3) [(1,0),(2,1),(3,2)]
Prelude Data.Array> print $ elems a
[0,1,2]
ここでは何かと便利な関数、accumArray の動作を説明しましょう。
accumArray (+) 0 (1,3) [(1,2),(3,4),(1,5)]
という式を実行すると、(1,3)
で定義域の指定された配列が
作られます。すなわち、配列の添字は 1 から 3 の範囲になります。配列の
各要素は accumArray の第2引数の 0
で初期化されます。この
配列を仮に a
と名付けます。続いて (1,2)
が処理
されます。タプルの第1要素の 1 を配列の添字として a!1
を
読み込みます。そして、タプルの第2要素の 2 を使って a!1 + 2
を計算して新しい a!1
の値とします。+
は
accumArray の第1引数として指定された関数です。同様に (3,4),(1,5)
も処理され、配列 a
は
array (1,3) [(1,7),(2,0),(3,4)]
になります。
次に、accum 関数の動作を説明しましょう。accum は配列生成時の初期値を 単一の値として与えるのではなく、既にある配列を初期値として与えます。 それ以外は、ほぼ accumArray と同じです。
Prelude Data.Array> let a = array (1,3) [(1,0),(2,1),(3,2)]
Prelude Data.Array> print $ accum (+) a [(1,4),(2,5)]
array (1,3) [(1,4),(2,6),(3,2)]
配列の定義域 (1,3)
は a
に含まれていますので、
accum 関数の引数に書く必要はありません。