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:" shigemk 2 "Nice to meet you, " SHIGEMK 2 SHIGEMK 2

その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 :R 1 Input a value to be used instead. ABORT :R 2 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 :R 1 Input a value to be used instead of ( FDEFINITION ' FORMAT-HARDDRIVE ) . RETRY :R 2 Retry STORE-VALUE :R 3 Input a new value for ( FDEFINITION ' FORMAT-HARDDRIVE ) . ABORT :R 4 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の概念を元に導かれている

まとめ