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

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

Common Lispを頑張る(12)

木曜日はノー残業デーです。
本日もCommon Lispをやっていきます。

昨日の醜態で大反省しました。先に進む前に基礎をやり直します。
なんか一冊の本をずっと読んでいると飽きてしまう性分なので、
新しく本を買ってみました。「実践Common Lisp」です。
これまた良書として名高いようです。ネットでは無料で読めます。
英語だし本のほうが内容が少々新しいそうなので本を買いました。

いきなり4章から入ってみます。それまでは大体Lispの紹介です。
とはいってもいきなりマクロとか紹介してくる辺り、
流石「実践」を謳っているだけのことはあります。
でもそのおかげで「Lispすげー!」となるので良い本ですね。
前置きでした。4章、「シンタックスとセマンティクス」読んでいきます。

Lisp処理系のブラックボックス

ほとんどの言語では、言語処理系の動作はブラックボックス化されているそうです。
仕様に則って書かれたテキストをそのブラックボックスに打ち込むと、
インタプリタなら指示したとおりにプログラムを実行し、
コンパイラなら実行した時に指示したとおりに動くようなプログラムを生成します。

大体の言語処理系のブラックボックスは、プログラムのテキストを動作や
オブジェクトコードへと変換する作業を一部分ずつ担うサブシステムに分割できるそうです。
よくあるのは言語処理系を3つのフェーズに分割し、それぞれの出力結果を
次のフェーズに流し込む方法だと書かれています。
■3つのフェーズ(上から下へ)
字句解析器:連なっている文字を字句へと落とし込む。
パーサ:言語の文法に基づいてプログラムにおける式を表す木(抽象構文木)を作る。
評価器:直接実行したり、他の言語(機械語等)にコンパイルする。

うおお、言ってることはわかるのですが、具体的なイメージができません。
抽象構文木が悪いですね。調べます…。
よっしゃ。調べたら一発でした。3 + 5ならこういうことですね。

    +
  /   \
3       5

多分これで合ってると思うので先に進みます。

先程までは一般的な言語の話でしたが、ここからCommon Lispの話です。
Common Lispではブラックボックスは2つあるといいます。
読取器:テキストからLispオブジェクトを生成するもの。
評価器:オブジェクトが表現する言語のセマンティクスを実装するもの。

読取器はどのようにして文字列がS式(Lispオブジェクト)へと変換されるかを決めます。
評価器はS式から組み立てられるLispのフォームを決めます。
ちょっとココらへんの説明はさっぱり理解できる気がしない(特に読取器)のでいったん置いといて進みます。
次のページがS式の説明なのでそこでなんとかなるといいなあ。

ブラックボックスが2つに別れていることからいくつかわかることがあるそうです。
・S式がプログラムのソースコードとしてだけでなく、
 外部に置くデータの書式としても使える。
・そしてそのデータの読み込みにはREADが使える。
・セマンティクスが文字の列ではなくオブジェクトの木によって定義されているため、
 文字列として書くはずのコードを言語内で簡単に定義することができる。
これは、以前やった同図象性というやつですかね。
最後のやつがマクロの基本らしいです。

基礎に立ち返る、みたいなこと言って始めたら想像以上に基礎でした。
とはいえLispにおける悟りはここらへんをしっかり理解することでしか
得られないような予感がします。やる気を出してまだまだ行きます。

S式

S式の基本的な構成要素はリストとアトムです。前もやった気がします。
リストは括弧で括られていて、空白で区切っていくつでも要素を含めます。
アトムは、リスト以外です。空リスト、nilはリストでありアトムだそうです。

リストについては上記で全てらしいです。
あと知っておくべきルールは様々な種類のアトムのルールだけだそうです。

・数値
数値は、数値です。+がついてても-がついてても、
/が入っている分数でも指数表記で終わっていても全部数値です。
1/4と2/8は等しく、1.0e0も1.0も等しいですが、
1と1.0と1.0d0は全て浮動小数点数と整数の違いで全て(基本は)異なるそうです。

・文字列
ダブルクオートで囲まれたものです。
"と\だけは\でエスケープする必要がありますが、
それ以外の文字は全て問答無用で文字列にすることができます。

・シンボル
Lispプログラムで使うprintだの*objects*といった名前は、
シンボルと呼ばれるオブジェクトで表現されています。
それが関数なのか変数なのかは関係なく、読取器は文字が連なったものを読んで
名前を表すためのオブジェクトを作るだけです。
流石にシンボルにするものにもルールはありますが、
使えない文字はそりゃそうだろうってものばかりなので特に感想はありません。
読取器は読んだ文字全てを大文字に変換します。
また、読取器は名前を読み取るとその名前のシンボルが『パッケージ』と呼ばれる
表に存在しているかどうかを調べます。
見つからなければその名前のシンボルをパッケージに追加します。
見つかったらそのシンボルを返します。
これでどんなS式のどこに同じ名前が現れてもそれを示すのに
1つのオブジェクトが使われます。

LispフォームとしてのS式

タイトルから察するに、評価器の話ですね。
読取器が作ってくれたS式はLispのコードとして評価できます。
でも全部が評価できるわけではありません。
どんなS式がLispフォームとして評価できるのかを決めるのが評価器です。
どんなものが評価できるのか。シンプルに、任意のアトム及びリストの
戦闘要素がシンボルになっているリストだそうです。

参考書は眠くなってきたこちらの気分を察してくれたのか、
面白いのはこんな話じゃないよね、どう評価されるのが知りたいよね、と
話を続けてくれます…いい本だなあ。
説明のためにざっくりと評価器をwell-formedなフォームを受け取って
値を返す機能だと定義します。

最もシンプルなLispフォームはアトムで、アトムは2種類あります。
シンボルと、それ以外です。
このときシンボルは変数の名前とされ、その変数の現在の値として評価されます。
シンボル以外は、全て自己評価型のオブジェクトです。
渡したものがそのまま返ってきます。例を見るまでもないですね。
自己評価型のシンボル(変数の名前にシンボルそのものを代入したもの)の代表例は
TとNIL、そしてキーワードシンボルだそうです。
キーワードシンボルは名前の先頭に「:」がついたもので、
読み取り機は:から始まる名前をインターンするとき、
そのシンボルを値として持つその名前の定数を自動的に定義するそうです。
これは…そうなの?試してみます。

CL-USER> :keyword
:KEYWORD
CL-USER>

マジでした。

リストが評価される方法

世紀のリストフォームは全てシンボルで始まるわけですが、
評価のされ方によって3種類のリストフォームに分けられるそうです。
分ける決め手は、リストの先頭のシンボルが関数名であるか、
マクロ名であるか、特殊オペレータの名前であるかです。
まだ定義されていないシンボルが先頭に置かれていた場合は、
関数の名前だとみなされます。

ここからそれぞれのフォームの説明に入るわけですが、
長くなってしまいました。流石にここで切ります。

突然始まってしまった言語仕様のお話ですが、しばらく続けたいと思います。
まあ飽きたらまた楽しくLispエイリアンの薫陶を受けようと思います。
僕は土曜日出勤しなくてはいけないのでアレですが、
明日は三連休前の金曜日です。あと一日やり過ごしましょう。