配列モジュール
SMLの配列モジュールを紹介します.
ML系はみんなそうだと思いますが, リストの方が柔軟な操作が可能ですので
パフォーマンス上の理由以外に配列を使うことは無さそうです.
(あとは長さを明示すると分かりやすい時に使うかも知れない?)
Vector vs Array
SMLでは配列モジュールとして Vector と Array が
Basisライブラリ(標準ライブラリ)の一部として提供されています.
- Vector: 多相的で定数時間アクセスを提供するimmutable配列
- Array : 多相的で定数時間アクセスを提供する mutable配列
基本的にはupdate関数の振る舞いが異なるだけでほぼ同じ振る舞いをします.
構築
普通のリストから構築またはtabulate関数により構築します.
VectorとArrayは全く同様に構築できます.
Vector
- val v0 = V.fromList [1,2,3,4,5]; (* リストから構築 *) val v0 = #[1,2,3,4,5] : int vector - val v1 = V.tabulate (10, fn x=>x*2); (* ジェネレータ関数を渡して構築 *) val v1 = #[0,2,4,6,8,10,12,14,16,18] : int vector
Array
- val a0 = A.fromList [1,2,3,4,5]; (* リストから構築 *) val it = [|1,2,3,4,5|] : int array - val a1 = A.tabulate (10, fn x=>x*2); (* ジェネレータ関数を渡して構築 *) val a1 = [|0,2,4,6,8,10,12,14,16,18|] : int array
要素へのアクセス
これもVectorとArrayで全く同様に扱うことが出来ます.
- V.sub (v0, 0); (* 0番目の要素へアクセス *) val it = 1 : int - A.sub (a0, 0); (* 0番目の要素へアクセス *) val it = 1 : int - V.sub (v0, 5); (* 範囲外アクセスをすると例外(subscript)が投げられる *) uncaught exception Subscript [subscript out of bounds] raised at: stdIn:18.1-18.6 - A.sub (a0, 5); (* 範囲外アクセスをすると例外(subscript)が投げられる *) uncaught exception Subscript [subscript out of bounds] raised at: stdIn:22.1-22.6
範囲外にアクセスすると必ず例外が投げられます.
更新
ここまでは違いはありませんでしたが, この部分が大きく異なります.
Vectorはimmutableなのでupdateを呼び出すと, 指定した要素が書き換わったvectorが新たに構築されて返りますが,
Arrayはupdate関数が直接渡した値を書き換えます.
Vector
- v0; val it = #[1,2,3,4,5] : int vector - V.update (v0, 0, 10); (* 0番目の要素を10したvectorを新たに作る *) val it = #[10,2,3,4,5] : int vector - v0; (* 元の値は変わっていない *) val it = #[1,2,3,4,5] : int vector
Array
- a0; val it = [|1,2,3,4,5|] : int array - V.update (v0, 0, 10); (* 0番目の要素を10に書き換える *) val it = #[10,2,3,4,5] : int vector - A.update (a0, 0, 10); val it = () : unit (* unitが戻ってくる *) - a0; val it = [|10,2,3,4,5|] : int array (* 値が書き換わった! *)
op=による比較
Vectorは格納している値が全て等しいかをチェックしますが,
Arrayの場合は同一の実体を比較したときのみtrueとなります.
- val xs0 = V.tabulate (5, fn x=>x); val xs0 = #[0,1,2,3,4] : int vector - val xs1 = V.tabulate (5, fn x=>x); val xs1 = #[0,1,2,3,4] : int vector - xs0 = xs1; val it = true : bool (* 内容が等しければtrue *)
- val xs0 = Array.array (10, 1); - val xs1 = xs0; (* 直接コピーすると *) - xs0 = xs1 val it = true : bool (* 等しい *) - val xs0 = Array.array (10, 1); - val xs1 = Array.array (10, 1); (* 別々に構築すると *) - xs0 = xs1 val it = false : bool (* 格納している値は等しいけど実体が異なる *)
Unsafe Vector/Array
SML/njにはUnsafe.Vector/Arrayというstructureも用意されています.
signature UNSAFE_VECTOR = sig val sub : ('a vector * int) -> 'a val create : (int * 'a list) -> 'a vector end signature UNSAFE_ARRAY = sig val sub : ('a array * int) -> 'a val update : ('a array * int * 'a) -> unit val create : (int * 'a) -> 'a array end
これらはそれぞれ, VectorとArrayから境界チェックを無くしたものなので,
(Unsafeじゃない方の)Vector/Arrayの値に適用することもできます.
「できます」というかそのためのインターフェースですね.
- UnsafeVector.sub (v0, 3); (* Vectorにunsafe関数を適用 *) val it = 4 : int - UnsafeArray.sub (a0, 4); (* Vectorにunsafe関数を適用 *) val it = 5 : int