Time-stamp: <2012-06-21 Thu 18:11:36 murayama>
- 11.1 ベクタ
- 11.2 特殊ベクタ
- 11.3
- 11.4
- 11.5
- 11.6
- 11.7
- 11.8
- 11.9
- 11.10
- 11.11 ハッシュテーブル
- 11.12 ハッシュテーブル上の反復
ベクタ
1次元配列のこと。
整数でインデックスされたコレクション。
2種類のベクタがある。
固定サイズのベクタ
CL-USER> (vector)
#()
CL-USER> (vector 1)
#(1)
CL-USER> (vector 1 2)
#(1 2)
#(...)はベクタのためのリテラル表記。
ベクタに変更を加えるつもりなら,vector関数よりmake-arrayを使う。
make-arrayはvectorより汎用な関数。
固定サイズと可変サイズ,任意次元の配列を作れる。
make-arrayは,次元を指定する引数をとる。
:initial-element引数を渡すと,値を初期化した状態で作れる。
CL-USER> (make-array 1 :initial-element nil)
#(NIL)
CL-USER> (make-array 3 :initial-element nil)
#(NIL NIL NIL)
可変サイズ(任意個ではない)のベクタを作るには,フィルポインタを持たせる。
フィルポインタは「次の要素を追加したときに埋める(fill)位置」という意味。
CL-USER> (make-array 5 :fill-pointer 0)
#()
可変ベクタに要素を追加するには,vector-pushを使う。
フィルポインタの値が示す場所に要素を追加し,フィルポインタの値を1つ増
やし,新しく追加された要素のインデクスを返す。
CL-USER> (defvar *v* (make-array 5 :fill-pointer 0))
*V*
CL-USER> (vector-push 'a *v*)
0
CL-USER> *v*
#(A)
CL-USER> (vector-push 'b *v*)
1
CL-USER> *v*
#(A B)
CL-USER> (vector-push 'c *v*)
2
CL-USER> *v*
#(A B C)
要素を取り出すにはvector-popを使う。
CL-USER> (vector-pop *v*)
C
CL-USER> *v*
#(A B)
CL-USER> (vector-pop *v*)
B
CL-USER> *v*
#(A)
CL-USER> (vector-pop *v*)
A
CL-USER> *v*
#()
可変サイズのベクタ
任意のサイズに変更できるようにするには,
キーワード変数「:adjustable」を追加する。
(make-array 5 :fill-pointer 0 :adjustable t)
必要に応じてサイズを変えられる可変ベクタが作られる。
要素の追加には,vector-push-extendを使う。
動作はvector-push-extendと同様だが,
要素がいっぱいのベクタを呼び出した際には,自動的に配列を拡張する。
CL-USER> *v*
#(A B C)
CL-USER> (vector-push-extend 'd *v*)
3
CL-USER> *v*
#(A B C D)
特殊ベクタ
任意の型のオブジェクトを持てるベクタではなく,
特定の型の要素だけを保持するベクタ。
コンパクトにデータを保存できるかもしれないし,
汎用ベクタより要素へのアクセスが高速になるかもしれない。
そのような特殊ベクタとして,よく登場するのは文字列。
:element-type引数に指定することで作れる。
例(初期サイズが0の空の文字列)
CL-USER> (make-array 5 :fill-pointer 0 :adjustable t :element-type 'character)
""
CL-USER> (setf *v* (make-array 5 :fill-pointer 0 :adjustable t :element-type 'character))
""
CL-USER> (vector-push #\a *v*)
0
CL-USER> *v*
"a"
CL-USER> (vector-push #\b *v*)
1
CL-USER> *v*
"ab"
他の特殊ベクタとして,ビットベクタがある。
ビットベクタは全ての要素が0もしくは1である。
2つのビット配列の論理積を取るなどのビット操作のライブラリがたくさんあ
る。(ここでは紹介しない)
ビットベクタを作るには:element-typeに'bitを渡す。
ハッシュテーブル
キーと値のペア(エントリ)を要素に持つテーブル。
引数なしのmake-hash-tableは,eqlによって同じオブジェクトだった場合に
2つのキーが等値だと判断するハッシュテーブルを作成する。
キーとして文字列を使わないならデフォルトの動作で問題はないが,
2つの文字列が同じないようかどうかを判断するのにeqlは使えない。
その場合にはequalを使いたい。
そのためにmake-hash-tableのキーワード引数:testにシンボルequalを渡す。
その他に指定できる値はeq, eql, equal, equalpだけである。
ハッシュテーブルの要素にアクセスするにはgethash関数を使う。
(gethash キー ハッシュテーブル)
返り値は2つある。
1つめの返り値は,キーに対応する値があればその値を返し,
対応する値がなければnilを返す。
2つめの返り値は,キーがハッシュテーブルに出現するときにtを返し,
出現しない時にnilを返す。
こうしないとキーがあって値がnilのときに対応できない。
CL-USER> (defvar *h* (make-hash-table))
*H*
CL-USER> (gethash 'foo *h*)
NIL
NIL
CL-USER> (setf (gethash 'foo *h*) 'quux)
QUUX
CL-USER> (gethash 'foo *h*)
QUUX
T
複数の返り値を見るためのフォームを使えば,2つ目の値を見られるが,
あえて使わない限りは無視される。
多値の簡単な例を示す。
hashの追加の値を使うために,multiple-value-bindマクロを使う。
multiple-value-bindはletのように変数束縛を作り,
フォームが返した多値を使ってそれらを埋める。
(defun show-value (key hash-table)
(multiple-value-bind (value present) (gethash key hash-table)
(if present
(format nil "Value ~a actually present." value)
(format nil "Value ~a because key not found." value))))
CL-USER> (setf (gethash 'bar *h*) nil)
NIL
CL-USER> (show-value 'foo *h*)
"Value NIL because key not found."
CL-USER> (show-value 'bar *h*)
"Value NIL actually present."
CL-USER> (show-value 'baz *h*)
"Value NIL because key not found."
テーブルからエントリを削除する場合,
キーに対応する値をnilにするだけではテーブルに値が残る。
エントリを完全に取り去るには,remhashを使う。
gethashと同じ引数をとり,対応するエントリを削除する。
clrhashはハッシュテーブルの全ての値とキーを削除する。
ハッシュテーブル上の反復
maphashは関数とハッシュテーブルを引数に取り,
ハッシゅテーブル内のエントリ(キーと値の各ペア)に対して関数を呼び出す。
例(ハッシュテーブル中の全てのエントリを印字する)
CL-USER> (maphash #'(lambda (key value) (format t "~a => ~a~%" key value)) *h*)
BAR => NIL
FOO => 1
BAZ => 2
NIL
例(2より小さい値を持つエントリを全て削除する)
(maphash #'(lambda (key value) (when (< value 2) (remhash key *h*)) *h*))
反復処理をするには拡張したloopを使う方法もある。
上記の例と等価な例をloopで表現したものは以下となる。
(loop for key being the hash-keys in *h* using (hash-value value)
do (format t "~a => ~a~%" key value))
最終更新:2012年06月21日 18:23