Land of Lisp 5章メモ

5章メモ

テキストゲームを作る。 世界は場所をノード、その間の移動手段をエッジとした有向グラフとして捉えられる。 ゲームでは以下の操作を行える。

  • 周囲を見る
  • 移動する
  • オブジェクトを拾う
  • オブジェクトを使う

文字列とシンボルのリスト

ここでは文字列でなくシンボルのリストをつかってdescriptionを書いている。 構造を持ったデータを扱う練習みたいなもんだろうか。

連想リスト

carにキー、cdrに値をもったKey-Valueペアのリストで連想リストを表現する。 assoc関数で取り出す(KVPが返る)。

有向グラフの表現

ゲーム世界の有向グラフをノードのリストと、エッジのリストで表す。ノードのリストはノード名をキーとし、descriptionを値とした連想リスト。エッジのリストはノード名をキーとし、そのノードから出るエッジの行き先ノード、方向、エッジの実体のリストを値とした連想リスト。

準クオート

クオートされたリスト中に評価したい式を埋め込む構文。'のかわりに`でクオートし、,でアンクオートする。

`(quoted ,unquoted quoted)みたいな感じ。

高階関数

Common Lispでは関数と変数の名前空間が分かれているので、関数を引数などに渡すときには#'を付けて関数であることを指定する必要がある。

ここで出てきた高階関数

  • mapcar : いわゆるmap
  • apply : リストの要素をそれぞれの引数として関数を呼ぶ

述語

述語(A->bool)は末尾にpをつける習慣。

関数型プログラミングスタイル

ここでは副作用を伴わない関数を関数型スタイルとしてる。

push/assoc

pushはリストの先頭に要素を加える。 assocはリストの先頭から要素を探して最初の値を返すのでこれで更新したように扱える。

Source

本のコードでは出力文を1つのリストにしてるが、 後にいい感じのprintをする上でも1文ごとのリストのリストの方が良さそうなのでそうしている。

;ノード(場所)
(defparameter *nodes* `((living-room (you are in the living-room.))
                        (garden (you are in a beautiful garden.))
                        (attic (you are in the attic.))))

(defun describe-location (location nodes)
  (cadr (assoc location nodes)))

;エッジ(移動手段)
(defparameter *edges* `((living-room (garden west door)
                                     (attic upstairs ladder))
                        (garden (living-room east door))
                        (attic (living-room downstairs ladder))))

(defun describe-path (edge)
  `(there is a ,(caddr edge) going ,(cadr edge) from here.))

;元のコードだと全部1つのリストにしてるけど、1文ごとに分けたほうが構造保てて良さそうなので
(defun describe-paths (location edges)
  (mapcar #'describe-path (cdr (assoc location edges))))

;オブジェクト
(defparameter *objects* '(whiskey bucket frog chain))

(defparameter *object-locations* '((whiskey living-room)
                                   (bucket living-room)
                                   (chain garden)
                                   (frog garden)))

(defun objects-at (loc objs obj-locs)
  (flet ((at-loc-p (obj)
           (eq (cadr (assoc obj obj-locs)) loc)))
    (remove-if-not #'at-loc-p objs)))

(defun describe-objects (loc objs obj-locs)
  (flet ((describe-obj (obj)
           `(you see a ,obj on the floor.)))
    (mapcar #'describe-obj (objects-at loc objs obj-locs))))

;現在地
(defparameter *location* 'living-room)

(defun look ()
  (concatenate 'list
               (list (describe-location *location* *nodes*))
               (describe-paths *location* *edges*)
               (describe-objects *location* *objects* *object-locations*)))

;歩く
(defun walk (direction)
  (let ((next (find direction (cdr (assoc *location* *edges*)) :key #'cadr)))
          (if next
              (progn (setf *location* (car next))
                     (look))
              '((you cannot go that way.)))))

;オブジェクトを手に取る
(defun pickup (object)
  (list (cond 
          ((member object
                   (objects-at *location* *objects* *object-locations*))
           (push (list object 'body) *object-locations*)
           `(you are now carrying the ,object))
          (t '(you cannot got that.)))))

(defun inventory ()
  (list (cons 'items- (objects-at 'body *objects* *object-locations*))))