ストラクチャ
ストラクチャはいわゆる構造体だが,Lispのそれはなんかすごい.
ストラクチャはdefstructで作成できる.フィールドname,ageをもつ構造体personは以下のようにして作成できる.
> (defstruct person name age) PERSONLispプログラムはLispプログラムを作成できるが,defstructは良い例だろうか.この例ではdefstructを呼び出した時点で,以下の関数が同時に作成される.
- make-person
- 関数名が「make-構造体名」の形式の関数.該当する構造体を生成する.
- person-p
- 関数名が「構造体名-p」の形式の関数.引数が該当する構造体かどうかを判定する.
- copy-person
- 関数名が「copy-構造体名」の形式の関数.引数を該当する構造体とし,そのコピーを返す.
- person-name
- 関数名が「構造体名-フィールド名」の形式の関数.引数を該当する構造体とし,該当するフィールドの値を返す.
- person-age :構造体名-フィールド名
- 同上.
> (defstruct person name age) PERSON ;; personという構造体を作成し,name="HOGE",age=10で初期化. > (setf hoge (make-person :name "HOGE" :age 10)) #S(PERSON :NAME "HOGE" :AGE 10) > (person-p hoge) T > (person-name hoge) "HOGE" > (person-age hoge) 10 > (setf fuga (copy-person hoge)) #S(PERSON :NAME "HOGE" :AGE 10) ;; 例によって構造体のフィールドの値はsetfで設定できる. > (setf (person-name fuga) "FUGA") "FUGA" > hoge #S(PERSON :NAME "HOGE" :AGE 10) > fuga #S(PERSON :NAME "FUGA" :AGE 10)
「make-構造体名」で構造体の実体を生成できるが,その時にフィールド名の初期化値を指定できた.
> (defstruct person name age) PERSON ;; personという構造体を作成し,name="HOGE",age=10で初期化. > (setf hoge (make-person :name "HOGE" :age 10)) #S(PERSON :NAME "HOGE" :AGE 10)「make-構造体名」を呼び出すときに初期化値を指定しなければ以下のようになる.
> (defstruct person name age) PERSON > (make-person) #S(PERSON :NAME NIL :AGE NIL)ここではNILだが,それは処理系による.あとで初期化した方がよい.だが,うれしいことにdefstructの際にデフォルトの初期化値を指定することができる.
> (defstruct person (age 0) (name "who")) PERSON > (make-person) #S(PERSON :AGE 0 :NAME "who") > (make-person :age 100 :name "HOGE") #S(PERSON :AGE 100 :NAME "HOGE") > (make-person :name "HOGE") #S(PERSON :AGE 0 :NAME "HOGE") > (make-person :age 100) #S(PERSON :AGE 100 :NAME "who")もちろん「make-構造体名」を実行時に値を指定すればそれで初期化される.注意点としては,初期化値にlispが処理できない指定を行なってもdefstruct時には怒られず,「make-構造体名」によってその初期化値で埋められるときに怒られることである.
> (defstruct person (age 0) (name who)) PERSON [14]> (make-person ) *** - EVAL: variable WHO has no value > (make-person :name "HOGE") #S(PERSON :AGE 0 :NAME "HOGE")この例でいうとdefstruct時には(name who)は評価されず,「make-構造体名」でデフォルトの初期化値を埋める時に評価されると想像できる.すなわち他の関数と同様,(name who)の部分は任意の式にできる.
> (make-person :name "HOGE") #S(PERSON :AGE 0 :NAME "HOGE") > (defstruct person (age (progn (format t "age?") (read))) (name (progn (format t "name?") (read)))) PERSON > (make-person ) age?100 name?hoge #S(PERSON :AGE 100 :NAME HOGE)この例は初期化値を指定せずにmake-personを呼び出すとreadでユーザに問い合わせている.
defstructすると同時にいろんな関数が作成される.そのうちの一つに「構造体名-p」があった.つまり関数に加えて型も作成される.すなわち
> (defstruct person name age) PERSON > (setf hoge (make-person :name "HOGE" :age 10)) #S(PERSON :NAME "HOGE" :AGE 10) > (person-p hoge) T > (typep hoge 'person) Ttypepは汎用的な型チェック関数である.第一引数が第二引数のシンボルで表される型かどうかを判定する.すなわち上の例の最後の式はpersonという型があってhogeはその型の変数であることを示している.ちなみにclisp処理系ではtypepに存在しない型を指定すると以下のようになった.
> (typep hoge 'persoon) *** - TYPEP: invalid type specification PERSOONちなみにstructure型はatomでありtである.
> (defstruct person name age) PERSON > (setf hoge (make-person :name "HOGE" :age 10)) #S(PERSON :NAME "HOGE" :AGE 10) > (typep hoge 'atom) T > (typep hoge 't) T