by shigemk2

当面は技術的なことしか書かない

Land of Lisp 6章 メモ

Land of Lisp

第6章

  • ユーザーインターフェース/コマンドラインインタフェース

6.1 テキストの表示と読み込み

print vs prin1

[1]>  (print "foo")

"foo"
"foo"
[2]>  (progn (print "this")
 (print "is")
 (print "a")
 (print "test"))

"this"
"is"
"a"
"test"
"test"
[3]> (progn (prin1 "this")
 (prin1 "is")
 (prin1 "a")
 (prin1 "test"))
"this""is""a""test"
"test"

read

その1

[4]> (defun say-hello ()
  (print "Please type your name:")
  (let ((name (read)))
    (print "Nice to meet you, ")
    (print name)))
SAY-HELLO
[5]> (say-hello)

"Please type your name:" shigemk2

"Nice to meet you, "
SHIGEMK2
SHIGEMK2

その2

[6]> (defun add-five ()
  (print "please enter a number:")
  (let ((num (read)))
    (print "When I add five I get")
    (print (+ num 5))))
ADD-FIVE
(add-five)

[7]>
(add-five)

"please enter a number:" 3

"When I add five I get"
8
8
[8]> (add-five)

"please enter a number:" hoge

"When I add five I get"
*** - +: HOGE is not a number
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead.
ABORT          :R2      Abort main loop
Break 1 [9]> :q

princ

[10]> (princ '3)
3
3
[11]> (princ '"foo")
foo
"foo"

printと違ってダブルクオートは出ない

[12]> (defun say-hello ()
  (princ "Please type your name:")
  (let ((name (read-line)))
    (princ "Nice to meet you, ")
    (princ name)))
SAY-HELLO
[13]> (say-hello)
Please type your name:test
Nice to meet you, test
"test"

6.2 Lisp におけるコードとデータの対称性

  • 同図象性
    • プログラムコードとデータを同じデータ構造を使って扱うプログラミング言語は、同図象性 を持つと言われる

自己書き換えプログラム

[15]>  (defparameter *foo* '(+ 1 2))
*FOO*
[18]> (eval *foo*)
3
  • 経験未熟なうちに eval を使うのはセキュリティ上の危険を招く可能性もある

6.3 ゲームエンジンに専用のインタフェースを追加する

loop/read/print

[49]> (defun game-repl ()
 (loop (print (eval (read)))))
GAME-REPL
[50]> (game-repl)
(look)

(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH. THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE. YOU SEE A WHISKEY ON THE
 FLOOR. YOU SEE A BUCKET ON THE FLOOR.)
[20]> (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 nil)))))))
TWEAK-TEXT
(defun game-print (lst)
  (princ (coerce (tweak-text (coerce (string-trim "() "
                                                  (prin1-to-string lst))
                                     'list)
                             t
                             nil)
                 'string))
  (fresh-line))

[21]>
(defun game-print (lst)
  (princ (coerce (tweak-text (coerce (string-trim "() "
                                                  (prin1-to-string lst))
                                     'list)
                             t
                             nil)
                 'string))
  (fresh-line))
GAME-PRINT
[22]>  (game-print '(not only does this sentence have a "comma," it also mentions the "iPad."))
Not only does this sentence have a comma, it also mentions the iPad.
T

6.4 さあこの素敵なゲームインタフェースを試してみよう

[28]> (defun game-repl ()
  (let ((cmd (game-read)))
    (unless (eq (car cmd) 'quit)
      (game-print (game-eval cmd))
      (game-repl))))
GAME-REPL
[29]> (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))))))
GAME-READ
[30]> (defparameter *allowed-commands* '(look walk pickup inventory))
*ALLOWED-COMMANDS*
(defun game-eval (sexp)
  (if (member (car sexp) *allowed-commands*)
      (eval sexp)
    '(i do not know that command.)))

[31]>
(defun game-eval (sexp)
  (if (member (car sexp) *allowed-commands*)
      (eval sexp)
    '(i do not know that command.)))
GAME-EVAL
[32]> (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 nil)))))))
TWEAK-TEXT
(defun game-print (lst)
  (princ (coerce (tweak-text (coerce (string-trim "() "
                                                  (prin1-to-string lst))
                                     'list)
                             t
                             nil)
                 'string))
  (fresh-line))

[33]>
(defun game-print (lst)
  (princ (coerce (tweak-text (coerce (string-trim "() "
                                                  (prin1-to-string lst))
                                     'list)
                             t
                             nil)
                 'string))
  (fresh-line))
GAME-PRINT
[34]> (game-repl)
look
You are in the living-room. A wizard is snoring loudly on the couch. There is a door going west from here. There is a ladder going upstairs from here. You see a bucket on the
 floor.
walk west
You are in a beautiful garden. There is a well in front of you. There is a door going east from here. You see a frog on the floor. You see a chain on the floor.
pickup chain
You are now carrying the chain
scratch head
I do not know that command.
pickup chicken
You cannot get that.
walk west
You cannot go that way.
walk east
You are in the living-room. A wizard is snoring loudly on the couch. There is a door going west from here. There is a ladder going upstairs from here. You see a bucket on the
 floor.
walk upstairs
You are in the attic. There is a giant welding torch in the corner. There is a ladder going downstairs from here.
inventory
Items- whiskey chain
walk downstairs
You are in the living-room. A wizard is snoring loudly on the couch. There is a door going west from here. There is a ladder going upstairs from here. You see a bucket on the
 floor.
pickup bucket
You are now carrying the bucket
look
You are in the living-room. A wizard is snoring loudly on the couch. There is a door going west from here. There is a ladder going upstairs from here.
quit
NIL

6.5 read と eval の危険について

readとかevalとか使っていると高度なテクニックをつかうと邪悪なコードが実行できてしまう

[35]>  (game-repl)
format-harddrive
I do not know that command.
 walk (format-harddrive)
You cannot go that way.
 walk (format-harddrive)
You cannot go that way.
walk #.(format-harddrive)

*** - EVAL: undefined function FORMAT-HARDDRIVE
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of (FDEFINITION 'FORMAT-HARDDRIVE).
RETRY          :R2      Retry
STORE-VALUE    :R3      Input a new value for (FDEFINITION 'FORMAT-HARDDRIVE).
ABORT          :R4      Abort main loop
Break 1 [36]> :q

まとめ

  • コンソールを通して人間とコミュニケート
    • printとread
    • princやread-lineとか
  • 同図象性
    • クオート
    • 準クオート
    • eval
    • マクロ
  • 自分でカスタマイズされた REPL
  • Lisp 内部のデータ表現を、プログラムのインタフェースにふさわしい形式に変形するのは難しくない
    • プログラム内部で使うデータ構造と、その表示形式とを分けて考えることができる

第6.5章

6.5.1 lambda がすること

プログラムを書いていて、他の関数に「その場限りの処理」を渡したいなと思った時に、lambdaはぴったり

[37]> (defun half (n)
 (/ n 2))
HALF
[38]>  (lambda (n) (/ n 2))
#<FUNCTION :LAMBDA (N) (/ N 2)>
[39]>  (mapcar (lambda (n) (/ n 2)) '(2 4 6))
(1 2 3)

6.5.2 lambda がそんなに大事なわけ

  • Lisp は普通の言語と違い、ラムダ算法という数学的な概念から直接導かれたプログラミング言語
  • lambda形式は Lisp システムの中で最も根源的なコマンドで、Lisp の他の関数は lambdaの概念を元に導かれている

まとめ

  • lambda
  • 高階プログラミング

Land of Lisp

Land of Lisp