プログラミングを頑張る日記

プログラミングを勉強して、ハッカーになろう

Common Lispを頑張る(46)

また月曜日が始まりましたね。どうでもよいのです。
「実践Common Lisp」第11章、やっていきます。

Common Lispにも複数の値を1つのオブジェクトにまとめた標準的なデータ型があります。
特に有名なのはリストですね。

ところがこの本はリストをまだあんまり扱う気がないようです。
もっとそれぞれの場合により適したデータ構造があるのだから、
あんまりリストに特化した考えかたをしてほしくないという思いがあるよう。

そういうことなら自分も一旦リストを忘れて読んでいきます。

ベクタ

ベクタは整数でインデックスされたCommon Lispの基本的なコレクションです。
ベクタには2つの亜種がいるそうで、まず固定サイズのもの。
これはベクタの要素を保持している連続したメモリの塊の表面に薄い化粧板を張ってまとめたようなもの。
そして可変サイズ。要素の追加や削除に応じてベクタが伸縮し、実際のストレージを抽象化します。

特定の値を含む固定サイズのベクタはvector関数で作成できます。
vector関数は任意個の引数を取り、引数で与えた要素を含む固定サイズのベクタを新しく確保します。

CL-USER> (vector)
#()
CL-USER> (vector 1)
#(1)
CL-USER> (vector 1 2)
#(1 2)
CL-USER>

前にも書きましたが、#(...)というのはLispの読取器や印字器が使うベクタのためのリテラル表記です。
別に直接#(1 2)とコードに書いてしまってもいいけれど、
リテラルオブジェクトに変更を加えたときの動作は未定義だからvectorを使うって話ですね。

さて、もうmake-arrayという関数もあり、こちらはvactorよりも汎用の関数で、
固定サイズと可変サイズのベクタを作ることができます。
さらに、任意次元の配列を作ることができるそうです。

make-arrayには引数として、配列の次元を表すリストが必要であるそうです。
ベクタは1次元の配列なので、ベクタの場合はベクタのサイズを示す数を1つ含んだリストを引数に。
利便性のため、リストの代わりに数値を引数にすることもできます。
他に引数がなければ初期化されていない要素を含んだベクタを生成するそうで、
初期化されていない要素にアクセスしたとしたらその場合の動作も未定義です。やめます。
:initial-element引数を渡せば、その引数で値を初期化することができます。

CL-USER> (make-array 5 :initial-element 'neko)
#(NEKO NEKO NEKO NEKO NEKO)
CL-USER>

可変サイズのベクタは、固定サイズのベクタよりもちょっぴり複雑なオブジェクトだそう。
要素を保持するのに使っているメモリと空いているスロットを記録するのに加えて、
実際にベクタに保持している要素の数も当然記録していないといけないからです。
そしてこの数はベクタのフィルポインタとやらに記録されるそうです。

フィルポインタを持ったベクタを作るには、:fill-pointer引数を渡します。

CL-USER> (defparameter *vec* (make-array 5 :fill-pointer 0))
*VEC*
CL-USER> *vec*
#()
CL-USER>

サイズが変更できるベクタの末尾に要素を追加するにはvector-pushというのを使うそう。
フィルポインタの示す場所に要素を追加、フィルポインタの値を1つ増やし、
追加要素のインデックスを返してくれるということです。
vector-popというのは、最後に追加された要素を返し、フィルポインタの値を減らすと。

でもこれでは可変ベクタと呼ぶ気になりません。フィルポインタはいいのですが、要素数が固定では。
というわけで、任意のサイズに変更できるベクタを作るには:adjustable引数を追加します。
可変ベクタに限界を越えて要素を追加するには、vector-push-extendを使います。

CL-USER> (defparameter *vec* (make-array 5 :fill-pointer 0 :adjustable t))
*VEC*
CL-USER> (vector-push 'a *vec*)
0
CL-USER> *vec*
#(A)
CL-USER> (vector-push 'b *vec*)
1
CL-USER> (vector-push 'c *vec*)
2
CL-USER> *vec*
#(A B C)
CL-USER> (vector-pop *vec*)
C
CL-USER> *vec*
#(A B)
CL-USER> (vector-push 'd *vec*)
2
CL-USER> (vector-push 'e *vec*)
3
CL-USER> (vector-push 'f *vec*)
4
CL-USER> *vec*
#(A B D E F)
CL-USER> (vector-push 'g *vec*)
NIL
CL-USER> *vec*
#(A B D E F)
CL-USER> (vector-push-extend 'h *vec*)
5
CL-USER> *vec*
#(A B D E F H)
CL-USER>

久し振りに短くしめます。おやすみなさい。