gauche.array
- 配列 ¶このモジュールは多次元配列のデータタイプとそれに関する操作を提供します。 プリミティブなAPIはSRFI-25で定義されているものに従います。 任意のSchemeオブジェクトを保持できるSRFI-25の汎用配列の他に、 均一な数値ベクタ(ユニフォームベクタ参照)を使って 数値要素を効率良く保持する配列型も提供されます。 また、SRFI-10を使った配列の外部表現も実装されます。
N次元の配列の各エレメントはN
個の整数のインデックス
[ i_0 i_1 … i_N-1 ]
でアクセスされます。
配列は、各次元のインデックスの下限s_kおよび上限e_kを決める
shapeを持っています。ここで、s_k <= e_kであり、
k次元目のインデックスi_kは
s_k <= i_k < e_k を満たすものとします。
(s_k == e_k であるような配列も作れますが、
その配列にはデータをストアすることはできません。
また、0次元の配列は作れます。それは一つだけデータを保持できます)。
Shapeはそれ自体が [ D x 2 ] の配列です。
ここでDはそのshapeが表現する配列の次元数です。
配列のプリミティブに対しインデックスを渡すにはいくつか方法があります。 各インデックスをばらばらの引数として渡すこともできますし、 ベクタや1次元の配列にパックして渡すこともできます。 後者においてインデックスがパックされたベクタや配列を「インデックスオブジェクト」 と呼ぶことがあります。Gaucheでは、配列の要素に次々とアクセスするような処理では ベクタをインデックスオブジェクトとして使うと若干効率が良いでしょう。
配列はequal?
手続きで比較することが出来ます。
二つの配列のshapeが等しく、また対応する各要素がequal?
の意味で
等しい場合に二つの配列はequal?
であると見なされます。
内部的には、配列は1次元のインデックスでアクセスされるバッキングストレージと、 多次元のインデックスをバッキングストレージへのインデックスにマップする手続きとから 構成されています。
{gauche.array
}
配列に関する汎用操作を実装している、抽象ベースクラスです。
実際に配列のインスタンスを作るには、以下のいずれかの具体クラスを使って下さい。
{gauche.array
}
具体配列クラスです。<array>
クラスはSRFI-25互換の配列、
すなわち、任意のSchemeオブジェクトを格納できる配列を実装します。
<u8array>
から<f64array>
までは、
それぞれ<u8vector>
から<f64vector>
をバッキングストレージとして
用いる配列を実装し、制限された範囲の整数もしくは非正確な実数のみを
効率良く格納することができます。
#,(<array> shape obj …)
¶配列はこの形式で書き出されます。
(配列が例えば<u8array>
であるなら、<array>
の部分は<u8array>
となります。)
shapeは偶数個の整数のリストで、
2n番目の整数がn次元目のインデックスの下限を、2n+1番目の
整数がn次元目のインデックスの上限(+1)を表します。
その後に、配列の要素がrow-majorの順で書き出されます。
この構文が読み込まれると、もとの配列とequal?
である配列が作成されます。
; 次のような配列: ; 8 3 4 ; 1 5 9 ; 6 7 2 #,(<array> (0 3 0 3) 8 3 4 1 5 9 6 7 2) ; 4x4の単位行列 #,(<array> (0 4 0 4) 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1)
[SRFI-25]{gauche.array
}
objが配列であれば#t
が、そうでなければ#f
が返されます。
(is-a? obj <array-base>)
と等価です。
[SRFI-25]{gauche.array
}
Shapeがshapeである配列を作成します。
Shapeは [ D x 2 ] の配列で、
0 <= k < D なる各kに対して要素 [ k 0 ] は
要素[ k 1 ]以下でなければなりません。
initが与えられた場合は、配列の各要素がinitで初期化されます。
initが与えられなかった場合の配列の要素の初期値は不定です。
(make-array (shape 0 2 0 2 0 2) 5) ⇒ #,(<array> (0 2 0 2 0 2) 5 5 5 5 5 5 5 5)
{gauche.array
}
make-array
と似ていますが、均一な数値配列を返します。
{gauche.array
}
arrayと同じクラス、shape、内容を持つコピーを返します。
[SRFI-25]{gauche.array
}
偶数個の正確な整数を引数に取り、配列のshapeとして使える2次元の配列を返します。
(shape 0 2 1 3 3 5) ⇒ #,(<array> (0 3 0 2) 0 2 1 3 3 5) (shape) ⇒ #,(<array> (0 0 0 2))
[SRFI-25]{gauche.array
}
Shapeがshapeであるような配列を作成し、
その要素をinit …で初期化します。
(array (shape 0 2 1 3) 'a 'b 'c 'd) ⇒ #,(<array> (0 2 1 3) a b c d)
{gauche.array
}
array
と同様ですが、init …で初期化された
均一な数値配列を返します。
(u8array (shape 0 2 0 2) 1 2 3 4) ⇒ #,(<u8array> (0 2 0 2) 1 2 3 4)
[SRFI-25]{gauche.array
}
配列arrayの次元数を返します。
(array-rank (make-array (shape 0 2 0 2 0 2))) ⇒ 3 (array-rank (make-array (shape))) ⇒ 0
{gauche.array
}
配列arrayのshapeを表す配列を返します。
[SRFI-25+]{gauche.array
}
array-start
は配列arrayのdim番目の次元の
インデックスの下限を返します。
array-end
は上限+1を、そしてarray-length
は両者の差を返します。
array-start
とarray-end
はSRFI-25で定義されています。
(define a (make-array (shape 1 5 0 2))) (array-start a 0) ⇒ 1 (array-end a 0) ⇒ 5 (array-length a 0) ⇒ 4 (array-start a 1) ⇒ 0 (array-end a 1) ⇒ 2 (array-length a 1) ⇒ 2
{gauche.array
}
配列arrayの全要素数を返します。
(array-size (make-array (shape 5 9 1 3))) ⇒ 8 (array-size (make-array (shape))) ⇒ 1 (array-size (make-array (shape 0 0 0 2))) ⇒ 0
[SRFI-25]{gauche.array
}
配列arrayの要素を取り出します。最初の形式では、
要素は整数のインデックスk …で指定されます。
2番目の形式では、要素はベクタまたは1次元配列のインデックスオブジェクトindex
で指定されます。
[SRFI-25]{gauche.array
}
配列arrayの要素にvalueをセットします。
最初の形式では、
要素は整数のインデックスk …で指定されます。
2番目の形式では、要素はベクタまたは1次元配列のインデックスオブジェクトindex
で指定されます。
[SRFI-25]{gauche.array
}
Shapeがshapeであり、与えられた配列arrayとバッキングストレージを
共有する新しい配列を作成して返します。
procは、新しい配列へのインデックスを古い配列へのインデックスへ
マップする手続きです。新しい配列の次元数をn、古い配列の次元数をmと
した時、procはn個の引数を取りm個の値を返す手続きでなければ
なりません。さらに、各マッピングはaffineマッピング、すなわち、
出力は入力の線形合成(プラス定数)でなければなりません。
(share-array
はprocがaffineマッピングであるという事実に基づいた
最適化を行います。新しい配列にアクセスする度にprocが呼ばれるというわけでは
ありません)。
{gauche.array
}
arrayの各インデックスに対してprocを呼びます。
index引数が省略された場合は、
procはインデックス (i, j,k,…) に対して
(proc i j k …)
のように呼ばれます。
最初は各次元のインデックスの最小値から始まり、後の方の次元が優先的にインクリメントされます。
gosh> (define a (array (shape 0 2 0 2) 1 2 3 4)) a gosh> a #,(<array> (0 2 0 2) 1 2 3 4) gosh> (array-for-each-index a (^(i j) (print i","j))) 0,0 0,1 1,0 1,1
この形式の呼び出しは簡単なのですが、あまり効率が良くありません。 インデックスオブジェクトを省略可能引数indexに渡すことで、 より良い性能を引き出すことができます。 インデックスオブジェクトはループの度に、各インデックスを表す値に書き換えられます。 インデックスオブジェクトに使えるのは、変更可能な、ベクタ・1次元の配列・ s8vector・s16vectorあるいはs32vectorで、その長さは配列arrayの ランクと一致していなければなりません。インデックスオブジェクトを使うと、 ループ中に一切アロケーションが行われないため速度的に有利です。 ただし、ループの度にインデックスオブジェクトの内容が書き換えられることに 注意する必要があります。
gosh> (array-for-each-index a (cut format #t "~s\n" <>) (vector 0 0)) #(0 0) #(0 1) #(1 0) #(1 1) gosh> (array-for-each-index a (cut format #t "~s\n" <>) (s8vector 0 0)) #s8(0 0) #s8(0 1) #s8(1 0) #s8(1 1)
戻り値は未定義です。
{gauche.array
}
シェイプshapeが表現する全ての可能なインデックスに対してprocを呼びます。
省略可能なindex引数の動作はarray-for-each-index
と同様です。
戻り値は未定義です。
gosh> (shape-for-each (shape 0 2 0 2) (^(i j) (print i","j))) 0,0 0,1 1,0 1,1
{gauche.array
}
shapeが表現する各インデックスについてprocを呼び出し、
その戻り値から配列を構築して返します。省略可能なインデックスオブジェクトの用途は
array-for-each-index
と同じです。
次の例は与えられたシェイプに対する単位行列を生成します:
(tabulate-array (shape 0 3 0 3) (^(i j) (if (= i j) 1 0))) ⇒ #,(<array> (0 3 0 3) 1 0 0 0 1 0 0 0 1)
{gauche.array
}
配列arrayの各インデックスに対してprocを呼び、その戻り値で
arrayの要素を置き換えます。
省略可能なインデックスオブジェクトの用途は
array-for-each-index
と同じです。
二番目の呼び出し形式は第二引数にシェイプを取ります。
それはarrayのシェイプと一致しなければなりません。
意味的には冗長ですが、shapeがリテラルである場合、
何らかの最適化がなされる可能性があります。
戻り値は未定義です。
{gauche.array
}
引数array0, array1, …は同じシェイプを持つ
配列でなければなりません。各入力配列の対応する要素について、
それらを引数としてprocが呼ばれ、その戻り値から
新たな配列が作られて返されます。
二番目の呼び出し形式は第二引数にシェイプを取ります。
それは入力配列のシェイプと一致しなければなりません。
意味的には冗長ですが、shapeがリテラルである場合、
何らかの最適化がなされる可能性があります。
(array-map - (array (shape 0 2 0 2) 1 2 3 4)) ⇒ #,(<array> (0 2 0 2) -1 -2 -3 -4)
{gauche.array
}
array-map
と似ていますが、procの結果は
与えられたarrayに格納されます。arrayのシェイプは
入力配列のシェイプと同じでなければなりません。
戻り値は未定義です。
{gauche.array
}
arrayの全要素を並べたベクタもしくはリストを作って返します。
(array->vector (tabulate-array (shape 1 3 1 4) (^(i j) (+ (* 10 i) j)))) ⇒ #(11 12 13 21 22 23)
{gauche.array
}
指定の次元で配列を結合します。指定の次元の大きさは一致していなければなりません。
それ以外のシェイプは異なっていても構いません。配列のランクはいくつであっても
構いませんが、両配列のランクは同じでなければなりません。
;; [a b] [a b] ;; [c d] (+) => [c d] ;; [e f] [e f] (array-concatenate (array (shape 0 2 0 2) 'a 'b 'c 'd) (array (shape 0 1 0 2) 'e 'f)) ⇒ #,(<array> (0 3 0 2) a b c d e f) ;; [a b] [e] [a b e] ;; [c d] (+) [f] => [c d f] (array-concatenate (array (shape 0 2 0 2) 'a 'b 'c 'd) (array (shape 0 2 0 1) 'e 'f) 1) ⇒ #,(<array> (0 2 0 3) a b e c d f) ;; 結合次元の大きさが同じであればインデックスの範囲は異なっていてもよい (array-concatenate (array (shape 0 2 0 2) 'a 'b 'c 'd) (array (shape 1 3 0 1) 'e 'f) 1) ⇒ #,(<array> (0 2 0 3) a b e c d f)
{gauche.array
}
arrayはランク2以上の配列でなければなりません。
配列のdim1番目の次元とdim2番目の次元を転置します。
デフォルトは0番目と1番目です。
{gauche.array
}
arrayはランク2以上の配列でなければなりません。
配列のdim1番目の次元とdim2番目の次元をそれぞれ行と列とみなした行列を
考え、その行列を時計まわりに90度回転した新たな配列を作って返します。
;; [1 2 3] [4 1] ;; [4 5 6] => [5 2] ;; [6 3] (array-rotate-90 (array (shape 0 2 0 3) 1 2 3 4 5 6)) ⇒ #,(<array> (0 3 0 2) 4 1 5 2 6 3)
arrayのランクが2より大きい場合は、arrayは「部分配列の行列」 と考えられます。
{gauche.array
}
配列の内容を、指定番目の次元 (デフォルトは0) で裏返しにします。
array-flip!
はarrayを直接変更してそれを返します。
array-flip
はarrayには触らず、裏返した内容で新たな配列を作って返します。
;; [1 2 3] => [4 5 6] ;; [4 5 6] [1 2 3] (array-flip (array (shape 0 2 0 3) 1 2 3 4 5 6)) ⇒ #,(<array> (0 2 0 3) 4 5 6 1 2 3) ;; [1 2 3] => [3 2 1] ;; [4 5 6] [6 5 4] (array-flip (array (shape 0 2 0 3) 1 2 3 4 5 6) 1) ⇒ #,(<array> (0 2 0 3) 3 2 1 6 5 4)
{gauche.array
}
ランク2で行列ともにdimensionである単位行列を作って返します。
配列のクラスをclassに渡せば、結果はそのインスタンスになります。
デフォルトは<array>
クラスです。
(identity-array 3) ⇒ #,(<array> (0 3 0 3) 1 0 0 0 1 0 0 0 1) (identity-array 3 <f32array>) ⇒ #,(<f32array> (0 3 0 3) 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)
{gauche.array
}
arrayを行列とみなし、その逆行列を返します。
arrayは2次元で、正方行列となるシェイプを持っていなければなりません。
そうでない場合はエラーが投げられます。
arrayが正則行列でない場合は#f
が返されます。
{gauche.array
}
arrayを行列とみなし、その行列式を計算します。
arrayは2次元で、正方行列となるシェイプを持っていなければなりません。
そうでない場合はエラーが投げられます。
また、determinant!
は計算過程でarrayの内容を破壊します。
determinant
は計算の前にarrayをコピーするオーバヘッドが
ありますが、arrayは変更されません。
{gauche.array
}
配列aとbはともに2次元でなければなりません。
それらを行列とみなして乗算を行います。aの行数とbの列数は
一致していなければなりません。
;; [6 5] ;; [1 2 3] x [4 3] => [20 14] ;; [4 5 6] [2 1] [56 41] (array-mul (array (shape 0 2 0 3) 1 2 3 4 5 6) (array (shape 0 3 0 2) 6 5 4 3 2 1)) ⇒ #,(<array> (0 2 0 2) 20 14 56 41)
{gauche.array
}
arrayのpow乗を返します。arrayは正方行列、
powは非負の正確な整数でなければなりません。
{gauche.array
}
array-mul
の逆を行います。
array-div-left
は(array-mul B M)
がA
と
等しくなるような行列M
を、
array-div-right
は(array-mul M B)
がA
と
等しくなるような行列M
を返します。
AとBは2次元の正方行列でなければなりません。
Bが正則行列でない場合はエラーが通知されます。
{gauche.array
}
要素ごとの計算をする手続きです。2つ目以降の引数は、
最初の引数の配列と同じ形の配列か、数値でなければなりません。
数値の場合は、要素が全てその数値である、最初の引数の配列と同じ形の配列だと解釈されます。
要素ごとに加算、減算、乗算、除算を行い、結果を最初の引数の配列と同じ形の配列で返します。
!
で終わっている手続きは、最初の配列を結果を作るために再利用するかもしれません。
したがって最初の配列は変更可能である必要があります。ただし、必ず再利用されるとは
限らないので、呼び出し側は常に戻り値を使う必要があります。
(array-add-elements (array (shape 0 2 0 2) 1 2 3 4) (array (shape 0 2 0 2) 5 6 7 8) 10) ⇒ #,(<array> (0 2 0 2) 16 18 20 22) (array-div-elements (array (shape 0 2 0 2) 1 3 5 7) 100 (array (shape 0 2 0 2) 2 4 6 8)) ⇒ #,(<array> (0 2 0 2) 1/200 3/400 1/120 7/800)
ひとつだけ引数が渡された場合は、これらの手続きはその引数をそのまま返します。
形が同じであれば、異なる型の配列同士での演算もできます。 結果は最初の引数の配列の型になります。
(array-mul-elements (make-u8array (shape 0 2 0 2) 3) (array (shape 0 2 0 2) 1 3 5 7)) ⇒ #,(<u8array> (0 2 0 2) 3 9 15 21)