行列

ATSにおける行列とは単に2次元の配列です。 それは1次元の配列と列優先 (row-major) スタイルで表現されます (行優先 (column-major) スタイルではありません)。 型 T が与えられたとき、型 mtrxszref(T) は、 固有の列と行の数によって構成された行列で、行列中の要素は型 T です。 これ以降、型 mtrxszref(T) の値をおおざっぱに行列を呼びます。 混乱する可能性を明示的に避ける必要がある場合には、matrix0 値と呼ぶこともあります。

次元 m, n の行列 M が与えられたとき、 式 M[i, j] は (i, j) をインデックスとする M のセルの中身を取り出します。 このとき i と j は自然数で、それぞれ m と n よりも小さい値でなければなりません。 式 M[i, j] は左辺値としても使うことができます。 例えば (M[i, j] := exp) という代入は、式 exp を評価して値にした後、 その値をインデックス (i, j) で指し示した M のセルに書き込みます。

matrix0 値に関する様々な関数と関数テンプレートが matrixref.sats ファイルで宣言されています。 このファイルは atsopt によて自動的に読み込まれます。 例えば、行列に関する3つの関数テンプレートと2つの多相関数が次のようなインターフェイスで表現されています:

// fun{a:t@ype} mtrxszref_make_elt // template (row: size_t, col: size_t, x: a): mtrxszref (a) // fun mtrxszref_get_nrow{a:t@ype} (M: mtrxszref a): size_t // polyfun fun mtrxszref_get_ncol{a:t@ype} (M: mtrxszref a): size_t // polyfun // fun{a:t@ype} mtrxszref_get_elt_at // template (M: mtrxszref a, i: size_t, j: size_t): a // M[i,j] fun{a:t@ype} mtrxszref_set_elt_at // template (M: mtrxszref a, i: size_t, j: size_t, x: a): void // M[i,j] := x //

型 T と3つの値 nrow, ncol, init がそれぞれ 型 size_t, size_t, T で与えられた時、 mtrxszref_make_elt<T> (row, col, init) は型 mtrxszref(T) の行列を返します。 この行列の次元は nrow x ncol で、それぞれのセルは値 init で初期化されます。 型 T と型 mtrxszref(T) の行列 M が与えられたとき、 mtrxszref_get_nrow(M)mtrxszref_get_ncol(M) は M の列の数と行数を型 size_t でそれぞれ返します。 利便性のために mtrxszref_get_nrow(M)mtrxszref_get_ncol(M) はそれぞれ M.nrowM.ncol と書くこともできます。 行列へのアクセスと更新のために、 mtrxszref_get_elt_atmtrxszref_set_elt_at をそれぞれ呼び出すことができます。 利便性のために、これらの関数のかわりに角括弧表記を使うことができます。

例を見てみましょう。 次に定義する関数 mtrxszref_transpose は与えられた行列を転置行列にします:

fun{a:t@ype} mtrxszref_transpose (M: mtrxszref a): void = let // val nrow = mtrxszref_get_nrow (M) // fnx loop1 (i: size_t):<cloref1> void = if i < nrow then loop2 (i, 0) else () // and loop2 (i: size_t, j: size_t):<cloref1> void = if j < i then let val tmp = M[i,j] in M[i,j] := M[j,i]; M[j,i] := tmp; loop2 (i, j+1) end else loop1 (i+1) // end of [if] // in loop1 (0) end // end of [mtrxszref_transpose]

行列 M は正方形、つまり列と行の数が等しいことを仮定しています。 2つの関数 loop1loop2 は相互末尾再帰で定義されていて、 キーワード fnxloop1loop2 の中身を連結する必要があることを示していることに注意してください。 これらの関数の中にある相互末尾再帰呼び出しを、ローカルへの直接ジャンプへコンパイルすることができます。