初めての人のためのLISP[増補改訂版]のメモです。
最初:初めての人のためのLISP (第1講-第5講) - もうカツ丼でいいよな
シンボルの構造
印字名 | 属性リスト |
大域値 | 関数実体 |
印字名
- シンボルの名前。pnameとも。Common Lispではシンボル名(symbol name)。
- 印字名は文字列からなる
- ダブルクォートでくくったものを文字列とみなす("a" "abc" etc.)
- 文字列はシンボルと異なり単なる文字の並び以上の意味を持たない
- 文字列はシンボルと異なり同じ文字の並びでも実体が異なることがある
- 文字列の比較にはstring=を使う
(eq "abc" "abc") ;=> NIL (string= "abc" "abc") ;=> T
大域値
- 変数名と値の対応付けをする
- prog, letなどの中で宣言されていない変数が呼ばれたときもこの値を見る
- (setq a 5)は、シンボルaの大域値に5を表すLispポインタを書き込む
- 大域値が見つからないとき、unbound variable(束縛されていない変数)エラーが返る
属性リスト(property list)
- シンボルが何らかの属性とその値を辞書として持っているとする
- (get 'a 'b)でシンボルaの属性bの値が取り出せる
- symbol-plist関数でシンボルから属性リストを取り出せる
- get関数の定義(実際のgetには属性値が未定義の場合に返す値を指定する引数を第3引数として付け加えられる)
(defun get (symbol property) (assoc property (symbol-plist symbol)))
- 属性リストはalistではなく交代リストを使うLispが多い(今ではハッシュの方が多い)
- alistはドット対によって名前と値の対応付けをするが、交代リストでは単に名前と値が交互に出現する。
;; alist ((a . 1) (b . 2) (c 3 4 5)) ;; 交代リスト (a 1 b 2 c (3 4 5))
-
- 交代リストに対応するget
(defun get (symbol property) (let ((plist (symbol-plist symbol))) (loop (until (null plist) nil) ;; plistがnilになったら終わり (until (eq (car plist) property) (cadr plist)) ;; 要素がcarに見つかったらcadrを返す (setq plist (cddr plist)) ))) ;; plistを要素2つ分進める
(defun get (symbol property) (setq plist (symbol-plist symbol)) (get2 plist property)) (defun get2 (plist property) (cond ((null plist) nil) ((eq (car plist) property) (cadr plist)) (t (get2 (cddr plist) property)) ))
- 属性リストへの情報の追加はputprop、削除はremprop
;; 追加 (putprop 'シンボル '属性値 '属性名) ;; 削除 (remprop 'シンボル '属性名)
-
- ただしCommon Lispではより強力なsetfがあるので削除されており、(setf (get 'シンボル名 '属性名) '属性値)という具合に書く
- 今日的には属性リストは流行らず、オブジェクトを使うのが普通
apply
applyは第1引数に関数をもらい、第2引数にもらった引数のリストを関数に与えて評価する。この関数はapplyの引数として与えられる関数が変数であるような場合に重宝する。
(setq x 'cons) (apply x (list 3 4)) ;=> (3 . 4) (setq x (lambda (x y) (+ (* x x) (* y y)))) (apply x (list 3 4)) ;=> 25