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)

やっていることは以下の通り.

  1. 座標(10, 20, 30)を生成(構造体は, 既に定義してある).
  2. それの内容を表示.
  3. xの値だけ表示
  4. yの値を100に変更.
  5. 構造体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

参考にしたサイト