ファイヤープロジェクト
配列とベクタ
2003-10-24T06:00+09:00   matsu
Lispで配列を作ったり要素にアクセスしたりしてみた.そしてベクタについてもちょっと試してみた.
リストは最初のコンスから順にたどって要素にアクセス(シーケンシャルアクセス)するので,処理速度が気になる場合がある.配列だとインデクスを指定することで要素にアクセス(ランダムアクセス)できるので,配列を使いたくなることがある.配列は関数make-arrayで作成できる.
> (make-array '(5))
#(NIL NIL NIL NIL NIL)
上では1次元5要素の配列を作成している.この配列が2つある配列すなわち2x5の2次元配列を作成するには以下のようにする.
> (make-array '(2 5))
#2A((NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL))
さらにこの2次元配列が2つある3次元配列を作成するには,以下のようにする.
> (make-array '(2 2 5))
#3A(((NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL)) ((NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL)))
引数のリストの要素数が次数で(※),リストの頭の要素を最高次として各次元での配列の要素数を指定する.初期値は処理系依存(上の場合はたまたまnil)なので,自分で明示的に初期化するまでは使用しないほうがよい.Common Lispでは最低7次元,各次元の要素数は最低1023がもてるらしいが,私の環境ではそこまでデカいものはダメらしい.
> (make-array '(1023 1023 1023 1023 1023 1023 1023))

*** - MAKE-ARRAY: dimensions (1023 1023 1023 1023 1023 1023 1023) produce too large total-size
初期値を指定して配列を作成するには,キーワード引数initial-elementを使用する.
> (make-array '(2 3) :initial-element 'hoge)
#2A((HOGE HOGE HOGE) (HOGE HOGE HOGE))
上では2x3の配列を作成して,各要素をhogeで初期化している.
※次数が1の時はリストでなく数字を指定することができる.
> (make-array '(3))
#(NIL NIL NIL)
> (make-array 3)
#(NIL NIL NIL)
先の例の出力では,1次元の場合は#,n次(n>1)の場合は#nAと頭に出力されている(※).これから想像できるように,配列を直接表記するには,#naを使用する.
> #3a(((111 112 113) (121 122 123)) ((211 212 213) (221 222 223)))
#3A(((111 112 113) (121 122 123)) ((211 212 213) (221 222 223)))
この配列は当然make-arrayで作成した関数と同様に使用できる.また,この方法なら各要素毎に初期値を設定できる.
このように出力されない場合は大域変数*print-array*をtにする.逆にこのように出力させたくないならnilにする.
> (setf *print-array* t)
T
> (make-array '(2 2))
#2A((NIL NIL) (NIL NIL))
> (setf *print-array* nil)
NIL
> (make-array '(2 2))
#<ARRAY T (2 2)>
*print-array*がnilなら,配列の各次元の要素数が出力される.
配列の要素にアクセスするには,aref関数を使用する.
> (setf arr #3a(((111 112 113) (121 122 123)) ((211 212 213) (221 222 223))))
#3A(((111 112 113) (121 122 123)) ((211 212 213) (221 222 223)))
> (aref arr 0 0 0)
111
上の例では,3次元配列の要素(0,0,0)にアクセスしている.このように,arefは第一引数に配列をとり,それ以降にその配列の次数個の引数をとる(上の場合3次元配列なので3個の引数が続く).各値は,各次元のインデクスである.インデクスは0から始まる.
1次元の配列の出力は#(...)と他の次元の場合と異なっていた.
> #1a(1 2 3)
#(1 2 3)
> #2a((1 2) (3 4))
#2A((1 2) (3 4))
1次元の配列はベクタでもある.そして,上の出力はベクタとしての出力である.すなわちベクタは以下のように直接表記できる.
> #(1 2 3)
#(1 2 3)
ベクタはvector関数で作成することもできる.
> (vector 1 2 3)
#(1 2 3)
ベクタは1次元配列で,すなわちいろんな次元の配列の中の限定的なケース(=1次元のケース)である.だから,配列一般を扱うarefよりも高速な関数svrefがあっても不思議ではない(※).
> (setf vec #(1 2 3))
#(1 2 3)
> (svref vec 2)
3
第一引数がベクタで第二引数がインデクスである.インデクスは0から始まる.
※一般に機能や対象データが限定される関数程,高速である.
matsu(C)
Since 2002
Mail to matsu