Common Lisp勉強中・・・ (5)
本で言う第6章, 配列と構造体, ハッシュテーブル.
本は, 「やさしいLisp入門」.
配列
昔は, リストだけでデータ構造のほとんどを処理できるから, 配列はなかった.
でも, それでは記憶容量, 実行速度の面で不利が出る.
そのため, 配列が導入された.
リスト.
- 途中の要素を処理するために, 先頭から順番に辿っていく必要がある.
- 要素の数を簡単に増減させられる.
配列.
- 指定した位置にある要素を直接, 処理できる.
- 配列作成時点で決められた個数までしか利用できない.
配列の表記法.
#次元数a(要素1 要素2 ... ) #1a(x y z) ; 1次元配列, 1は省略可能 #2a((a b c) (d e f)) ; 2次元配列
関数make-arrayを使用した方法.
> (make-array 3) ;; 1次元配列 #(NIL NIL NIL) > (make-array '(2 2)) ;; 2次元配列 #2A((NIL NIL) (NIL NIL)) > (make-array '(3 3)) ;; 3次元配列 #2A((NIL NIL NIL) (NIL NIL NIL) (NIL NIL NIL)) > (make-array 2 :initial-contents '(x y)) ;; 初期化つき(各要素別々に指定) #(X Y) > (make-array 2 :initial-element 'a) ;; 初期化つき(全要素同じものを指定) #(A A)
あとは, initial-contentsとかの後に:element-typeとして, 要素の種類の指定もできる.
要素の参照.
> (setq x #1a(x y z)) #(X Y Z) > (aref x 0) X > (aref x 1) Y > (aref x 2) Z
構造体
リスト.
- 含まれるデータは, 出てくる順番だけを使って区別しなければならない.
- 参照は, 先頭から順番に行う.
- 要素の数は可変.
構造体.
- スロットと呼ばれる場所に要素が記憶される.
- 値の書き換えもスロットの名前を使う.
- 参照速度はほぼ一定.
- 要素の数は固定.
構造体の定義.
(defstruct 構造体名 (スロット名 初期値) ... ) > (defstruct coordinate ;; 座標coordinateを作成 (x 0) (y 0) (z 0)) COORDINATE > (make-coordinate :x 10 :y 20 :z 30) ;; 座標を(10, 20, 30)に設定 #S(COORDINATE :X 10 :Y 20 :Z 30)
#S(...)は, 「#S(構造体名 スロット名1 値1 スロット名2 値2 ... )」と読む.
構造体を定義して, 実際に使用するには, 記憶領域に確保する必要がある.
それには, 「make-構造体名」というコマンドを使用する.
スロットの参照, 値の再定義.
> (let ((xyz (make-coordinate :x 10 :y 20 :z 30))) (print xyz) (print (coordinate-x xyz)) (setf (coordinate-y xyz) 100) (print xyz)) #S(COORDINATE :X 10 :Y 20 :Z 30) 10 #S(COORDINATE :X 10 :Y 100 :Z 30) #S(COORDINATE :X 10 :Y 100 :Z 30)
やっていることは以下の通り.
- 座標(10, 20, 30)を生成(構造体は, 既に定義してある).
- それの内容を表示.
- xの値だけ表示
- yの値を100に変更.
- 構造体xyzの内容を表示.
参照には, 「構造体名-スロット名 構造体」と指定する.
スロット値の変更は, 上記のように, setfを使用して行う.
ハッシュテーブル
リスト.
- 記憶容量の許す限り保存できる.
- 動的に個数を増減できる.
- 参照する際, 先頭から順番に調べる.
ハッシュテーブル.
- 特定のデータに目印にして記憶させておいた値を参照.
- この目印をキーと呼ぶ.
- キーに関連づけたものをキーの値と呼ぶ.
- 記憶している要素数をいくらでも増やしていくことができる.
- 記憶する値の種類を限定しない.
- 同じキーに対応する値は1つ.
- キーの重複は許されない. 値の重複は許される.
- どこに記憶されている値でも一定の速度で参照できる. ただし, 高速に動作するかはキーの選び方しだい.
ハッシュテーブルの作成, 参照.
(make-hash-table) (let ((hashtest (make-hash-table :test #'equalp))) ;; キーワード引数:testを指定して, キーの一致判定に使用する関数をequalpに変更. (setf (gethash 'a hashtest) 100) ;; キー「a」に値100を設定. (setf (gethash 'b hashtest) 200) (setf (gethash 'c hashtest) 300) (print (gethash 'a hashtest)) ;; キー「a」に対応する値を検索, 出力. (print (gethash 'c hashtest))) 100 300 300
通常は, キーワード引数はいらないのかな.
実際は私もあまりよく分かってないしね.
さっきのと合わせて, ハッシュテーブルが現在記憶しているキーの数を表示する関数と, キー(と対応する値)を削除する関数.
(let ((hashtest (make-hash-table :test #'equalp))) (setf (gethash 'a hashtest) 100) (setf (gethash 'b hashtest) 200) (setf (gethash 'c hashtest) 300) (print (gethash 'a hashtest)) (print (gethash 'c hashtest)) (print (hash-table-count hashtest)) ;; 現在ハッシュテーブルが保存しているキーの数を表示(3). (remhash 'a hashtest) ;; キー「a」と対応する値を削除. (print (hash-table-count hashtest))) 100 300 3 2 2