プログラミングを頑張る日記

プログラミングを勉強して、ハッカーになろう

Common Lispを頑張る(7)

火曜日です。もう疲れましたね…。
今日もまたLispです。「Land of Lisp」第6章に入ります。
この章ではコマンドラインインターフェースを扱っていくみたいです。

テキストの表示

スクリーンに直接テキストを表示する関数についてからです。
まずはprint関数。

CL-USER> (print "hoge")

"hoge" 
"hoge"
CL-USER>

最初の"hoge"がprint関数が表示したもの、2番目が戻り値ですね。
他にも色々表示するための関数はあって、prin1というのが一歩進んだLisperが使うらしいです。
そう言われると使いたくなりますね。

CL-USER> (progn (print "おはよう")
		(print "いい天気ですね"))

"おはよう" 
"いい天気ですね" 
"いい天気ですね"
CL-USER> (progn (prin1 "おはよう")
		(prin1 "いい天気ですね"))
"おはよう""いい天気ですね"
"いい天気ですね"
CL-USER>

prin1は改行をしないというのが違いみたいです。
改行しない分printよりも基本的な関数で、組み合わせの自由度が高いと。ふむふむ。
訳注によれば、print関数は値を表示した後に改行するのではなく、
値を表示する前に改行するらしいです。
今はあまり関係なさそうですが、知らないといつか思ったとおりの挙動をしなくて焦ったりするかもですね。
せっかくなので実験。

CL-USER> (progn (print "改行しない")
		(prin1 "当然改行しない"))

"改行しない" "当然改行しない"
"当然改行しない"
CL-USER> (progn (prin1 "改行しちゃう")
		(print "改行はこっちのせい"))
"改行しちゃう"
"改行はこっちのせい" 
"改行はこっちのせい"
CL-USER>

テキストの読み込み

次は入力を読み込む関数readを使ってみます。

CL-USER> (defun say-hello ()
	   (prin1 "あなたの名前を教えてください:")
	   (let ((name (read)))
	     (prin1 `(よろしく、 ,name さん))))
SAY-HELLO
CL-USER> (say-hello)
"あなたの名前を教えてください:"ねこ
(よろしく、 ねこ さん)
(よろしく、 ねこ さん)
CL-USER>

おお、楽しい…!

さて、print(prin1)とreadが使えるようになりました。
Common Lispで何かを表示したいなら、何か読み込みたいならこれらをまず考えるべきだそうです。
これらはコンピュータに優しく人間に優しくない関数ですが、
これらを使うことでLispで扱うほとんどのデータ型の情報を失うことなく
読み書きできるという利点があるそうです。

では人に優しい読み書きの関数は…もちろんありました!
まずはprincです。どれぐらい優しいのか確認します。

CL-USER> (print "hello, world!")

"hello, world!" 
"hello, world!"
CL-USER> (princ "hello, world!")
hello, world!
"hello, world!"
CL-USER>

うっとおしいダブルクォーテーションがなくなって表示されていますね。
そして優しい読み込み関数はread-lineです。
さっきの挨拶を書き直します。

CL-USER> (defun say-hello ()
	   (princ "あなたの名前を教えてください;")
	   (let ((name (read-line)))
	     (princ "よろしく、")
	     (princ name)
	     (princ "さん")))
WARNING: redefining COMMON-LISP-USER::SAY-HELLO in DEFUN
SAY-HELLO
CL-USER>
CL-USER> (defun say-hello ()
	   (princ "あなたの名前を教えてください;")
	   (let ((name (read-line)))
	     (princ "よろしく、")
	     (princ name)
	     (princ "さん")))
WARNING: redefining COMMON-LISP-USER::SAY-HELLO in DEFUN
SAY-HELLO
CL-USER> (say-hello)
あなたの名前を教えてください;ねこ
よろしく、ねこさん
"さん"
CL-USER>

なるほどって感じですね。
これだけだとread-lineの優しさをあまり感じられませんが、
文字列をダブルクォーテーションで囲んで文字列だと明示しなくても渡せるあたりが
優しさだそうです。最初の自分で書いたコードは埋め込みを使いたくて
シンボルを使ったから文字列じゃなくても渡せたんですかね。
気になるんですが、今日はもう体力がないので先へ進みます。

同図象性

Lispはプログラムコードとデータを同じデータ構造を使って扱うことができ、
そういった特徴をもつプログラミング言語は同図象性を持つと言われるそうです。
式をクオートでデータにすることもその一部だそうで。

CL-USER> (* 3 9)
27
CL-USER> '(* 3 9)
(* 3 9)
CL-USER>

でも、これだけでは主な構造をリテラルデータとして書かねばならずまだ自由度が低いそうです。
自分は、式そのものを扱う発想はLispを始めるまでなかったので目から鱗です。
まあ、式をデータにできるだけではそんなに嬉しくないですよね。
そして、「コードの一部を変数に入れておけたら?」と提案してきます。
そして変数に格納されているコードを実行する、それができるそうです。
それを可能にする強力なコマンドはevalというそうです。
ワクワクしてきました。やります。

CL-USER> (defparameter *kakezann* '(* 3 9))
*KAKEZANN*
CL-USER> *kakezann*
(* 3 9)
CL-USER> (eval *kakezann*)
27
CL-USER>

これだけだと*kakezann*の中に27を格納しているのとあまり変わりませんね。
とはいえ面白いコマンドです。
そんな読者の心を見透かすように、「乱用するなよ」と書かれてました。
マクロでいいところに初心者はevalを使っちゃうんだよな〜とあるんで、
早くマクロを知りたいところですね。とりあえずevalの乱用は避けます。

この章の残りでは、前の章で作ったゲーム専用のREPLを作るみたいですが、
キリが良いので今日はここまでとします。