Land of Lisp 6章メモ
6章メモ
5章のゲームにコマンドラインインターフェースを追加する。
コンソールへの表示
print
: 表示位置が行頭でなければ改行して値を出力して空白を出力prin1
: 単に表示
入出力いろいろ
print
とread
はLispの値をそのまま入出力できる。
すなわち文字列なら"
で囲われて、文字なら#\
がついて出力され、入力するときにはこれらをつける。
読みやすい形で出力するにはprinc
を使う。
また、文字列として入力するにはread-line
を使う。
読みやすい形への整形は単射ではない
> (princ "10") 10 > (princ 10) 10
ので、読みやすい形で入力された値をいつも適切な型で受け取るprinc
の逆関数は原理的に無理。
eval
データとしてのLispコードを実行する関数。
REPL
Read Eval Print Loop
(defun repl () (loop (print (eval (read)))))
なんというそのまま。素敵。
Read,Eval,PrintをカスタマイズすることでオリジナルのREPLを作れる。
ゲーム用REPL
- Read : 括弧と引数のクオートをつけることで入力を楽に
- Eval : ゲームのコマンド以外を弾く事で安全に
- Print : 内部データを整形して表示する
ここでのPrintは基本的な変換しかしていないが、内部データを扱いやすい形で持ち、
適切に変換して表示する構造は多くのプログラムで使える。
要はView。
作成したREPLの安全性
Readで引数をクオートして、Evalでゲームのコマンド以外の関数を弾いてるので基本的には任意のコードは実行できないが、
Readで組み込みのread
を使っているのでリーダマクロを使われると安全ではないらしい。
Source
5章で出力文を1文ごとのリストのリストとしているのでそれに合わせてgame-print
も変えている。
;ゲーム用のREPLを作成する (defun game-repl () (let ((cmd (game-read))) (unless (eq (car cmd) 'quit) (game-print (game-eval cmd)) (game-repl)))) ;括弧とクオートを付けなくてもコマンドを呼べるようにする (defun game-read () (let ((cmd (read-from-string (concatenate 'string "(" (read-line) ")")))) (flet ((quote-it (x) (list 'quote x))) (cons (car cmd) (mapcar #'quote-it (cdr cmd)))))) ;ゲームのコマンド以外を受け付けない (defparameter *allowed-commands* '(look walk pickup inventory)) (defun game-eval (sexp) (if (member (car sexp) *allowed-commands*) (eval sexp) '((i do not know that command.)))) ;1文毎のリストに構造を改変してるのでgame-printも改変 (defun game-statement-print (statement) (labels ((proper (fn elem) (if (stringp elem) elem (funcall fn (prin1-to-string elem)))) (capitalize (str) (coerce (let ((lst (coerce str 'list))) (cons (char-upcase (car lst)) (mapcar #'char-downcase (cdr lst)))) 'string)) (proper-capitalize (elem) (proper #'capitalize elem)) (proper-downcase (elem) (proper #'string-downcase elem))) (string-trim "()" (princ-to-string (cons (proper-capitalize (car statement)) (mapcar #'proper-downcase (cdr statement))))))) (defun game-print (statements) (princ (string-trim "()" (princ-to-string (mapcar #'game-statement-print statements)))) (fresh-line))