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

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

Common Lispを頑張る(33)

今日もやっていきます「Land of Lisp」。
本日は12章、ストリームについての説明の章をやります。

ストリームの種類

Common Lispと外の世界のリソースとやり取りするためには、ストリームを使います。
リソースの種類、ストリームの向きでストリームが分類できるようです。

リソースの種類による分類

ストリーム* リソース*
コンソールストリーム REPL
ファイルストリーム ディスク上のファイル
ソケットストリーム ネットワーク上の他のコンピュータ
文字列ストリーム Lispの文字列

文字列ストリームだけはCommon Lispの中だけで完結する特殊なストリームのようです。

向きによる分類

向きによる分類ったって、当然向きは入力か出力かですね。

出力ストリームの、基本操作は下記の2つだそうです。
・出力ストリームかどうかを調べる
・データをストリームへと送り出す

他のデータ型に比べるととってもシンプルです。
しかし基本操作をこれだけに制限していることこそが、ストリームをより便利にしているということです。
入力ストリームかどうか調べる、というのはoutput-stream-pを使う様子。そのままですね。

CL-USER> (output-stream-p *standard-output*)
T
CL-USER>

データをストリームへと送り出すのはwrite-charを使うということです。

CL-USER> (write-char #\x)
x
#\x
CL-USER> (write-char #\x *standard-output*)
x
#\x
CL-USER>

第二引数は送り出すストリームのようです。省略すれば標準出力へ。
参考書では戻り値はnilなのですが、自分の環境では渡した文字がそのまま返ってきています。
どっちが便利かって訊かれたらこっちのほうが便利な気がするのでまあ気にしないでいきます。
流石にこの戻り値使って判定したりしないでしょうし。

入力ストリームの基本機能も出力ストリームと同様シンプルなものです。
・入力ストリームかどうか調べる
・入力ストリームから要素を一つ取り出す

入力ストリームであるか調べるには、もう予測がつきますがinput-stream-pを使います。

CL-USER> (input-stream-p *standard-input*)
T
CL-USER>

入力ストリームから要素を取り出すには、read-charを使います。

CL-USER> (read-char)
1234
#\1
CL-USER>

1234と入力してエンターを押すと、先頭の要素である1が返ってきました。数字ですね。

ファイルの読み書き

Common Lispでファイルを読み書きする方法はいくつかあるそうですが、
一番いいのはwith-open-fileを使うことであるそうです。他のコマンドより安全だそう。
使ってみます。

CL-USER> (with-open-file (mystream "data.txt" :direction :output)
           (print "my data" mystream))
"my data"
CL-USER>

data.txtは下記のようになっていました。

"my data"

一行目の改行が気になる…。"my data"のあとに改行コードが無いのはまあいいんですけど。

まあいいや。今度は読み込んでみます。:directionキーワードの引数を:inputにすればいいですね。

CL-USER> (with-open-file (mystream "data.txt" :direction :input)
           (read mystream))
"my data"
CL-USER>

無事に読み込むことができました。じゃあdata.txtはあれでいいのか。

扱うデータ型を少し複雑にします。

CL-USER> (let ((animal-noises '((犬 . ワンワン)
                              (猫 . ニャーオ)
                              (カラス . カーカー)
                              (豚 . ブーブー))))
              (with-open-file (mystream "animal_noises.txt" :direction :output)
                (print animal-noises mystream)))
(( . ワンワン) ( . ニャーオ) (カラス . カーカー) ( . ブーブー))
CL-USER>

さてさて、animal_noises.txtの中身は…

((犬 . ワンワン) (猫 . ニャーオ) (カラス . カーカー) (豚 . ブーブー))

うん、alistがそのまま出力されています。
読み込んでみます。

CL-USER> (with-open-file (mystream "animal_noises.txt" :direction :input)
                (read mystream))
                
(( . ワンワン) ( . ニャーオ) (カラス . カーカー) ( . ブーブー))
CL-USER>

これらの書き込みと読み込みを使えば、作ったデータを一旦退避してあとで復元なんてことも簡単ですね。

短いですが、ここで切ります。出かけなきゃいけなくて…シラフで返ってきたら更新します。