あくまで自分用のメモ。
今後の実装の時に、簡単に振り返るための記事。
進めている本は、Land Of Lisp

- 作者: M.D. ConradBarski,Conrad Barski,川合史朗
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/02/23
- メディア: 大型本
- 購入: 1人 クリック: 18回
- この商品を含むブログ (19件) を見る
前回の記事の続き。
今回は、5章のテキストゲームの外界との接点を作る。
print系
今までのプログラムでは、外界との接点がなかった。
ただ処理するだけで、その結果を評価したりで値っぽいものを見えていたが、例えば、clisp file_name.lisp
みたいな感じでやった時に、
何もprintされなかった。これは、入出力のコードを今まで書いてなかったからで、この章ではそれらを学ぶとともに、gameに最適なユーザーインターフェースを実装していく。
(print "foo") ;; "foo" ;; "foo"
1個目が実際に表示した値。
2個目は、replが評価した結果。
prin1
(prin1 "foo") ;; これは、改行なし
prin1
というコマンドは、行末に改行がない。
Golang
でいうと、
fmt.Println
-> print
fmt.Printf
-> prin1
みたいな感じ。実際のプログラムでは、prin1
の方が用いられることが多い。
(defun say-hello () (print "Please type your name:") (let ((name (read))) (prin1 "Nice to meet you ") (print name))) (say-hello) ;; [6]> (say-hello) ;; "Please type your name:" bob ;; "Nice to meet you " ;; BOB ;; BOB ;; [7]> (say-hello) ;; "Please type your name:" "bob" ;; "Nice to meet you " ;; "bob" ;; "bob" (defun add-five() (print "please enter a number:") (let ((num (read))) (print "When I add five I get") (print (+ num 5)))) (add-five) (defun say-hello2 () (princ "Please type your name:") (let ((name (read-line))) (princ "Nice to meet you ") (princ name))) (say-hello2)
挙動に関しては、実際に動かして見ないとわからないと思うので、もし読者の方がいたら、コピペしてやってみることをお勧めする。
ゲーム上のユーザーインターフェース
eval
(defparameter *foo* (+ 1 2)) (eval *foo*)
すごく便利らしい。まだ使い方はわからないが、後述で、それらしき発見があった。
実装
game-repl
自分だけのREPLを作ってみる。 ゲームに専用のインターフェースを追加。
(defun game-repl() (loop (print (eval (read))))) (game-repl) ;; 下のバージョンは、ただの無限ループではない。 ;; よくわからないが、ユーザー入力がそのままコードになるということかな (defun game-repl2() (let((cmd (game-read))) (unless (eq (car cmd) 'quit) (game-print(game-eval cmds)) (game-repl2))))
にしても、こんなに再帰がすぐ出てくるのはいいな。
game-read
ここで読んでいるgame-readは、標準のreadにある不都合な二つの点を直すこと。
括弧をつけないとコマンドを入力できない。これは困るので、read-lineで読んだものに、こっち側で、()をつける
クォートをつけるのがめんどくさい
(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))))))
read-from-stringの入力とする文字列は、rread-lineで得たものにちょっと加工したデータ。
game-eval
;; コマンドの制限 (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.)))
この実装はそんなに難しいものではない。
game-print
要件は、「いい感じにプリントしたい。」
(defun tweak-text(lst caps lit) (when lst (let ((item (car lst)) (rest (cdr lst))) (cond ((eql item #\space) (cons item (tweak-text rest caps lit))) ((member item '(#\! #\? #\.)) (cons item (tweak-text rest t lit))) ((eql item #\") (tweak-text rest caps (not lit))) (lit (cons item (tweak-text rest nil lit))) (caps (cons (char-upcase item) (tweak-text rest nil lit))) (t (cons (char-downcase item) (tweak-text rest nil lit))))))) (defun game-print (lst) (princ (coerce (tweak-text (coerce (string-trim "() " (prin1-to-string lst)) 'list) t nil) 'strings)) (fresh-line))
この実装は少々複雑。
これは、実際に動かしてみるとわかるが、引数として渡ってきた文字列を、いい感じ(タブルクォーテーションなしで、大文字にすべきとこは大文字、そうでないものは小文字、のように。)
次は、6.5章、lambda.