実践CommonLispを読むその6

第七章 マクロ:標準的な制御構文の構築

ついにマクロですね。LISPの特徴的なものだと聞きかじっているんですけど・・・マクロとはC言語のようなマクロを意味するものや、メイン処理を補助する役目のマクロ等々使う言語によって沢山の意味合いがありますよね。今ではワードやエクセルにもマクロってのがありますから意味の範囲が広い気がする。

ではLISPで言うところのマクロとは、ん〜機能を拡張できるものと言えばいいのかな、例えば関数があるが、その関数では物足りない、では、その関数と同じような事をやらせながら処理を拡張する、それをマクロと言う。ん〜なんて言えばいいのだろうが、C言語では標準関数がありユーザ自身の作った関数もあるわけだが、ユーザの作った関数は言語を拡張する関数と言うわけじゃないよね。LISPでも標準関数があるわけだが、その標準関数の機能に物足りなさを感じたらマクロで言語を拡張し物足りなさを補うわけだ。

どうもC言語LISPでは言語を拡張する壁の高さが違う気がする。C言語では標準関数にしようと思えば多大なる労力が必要だと思うのだが、LISPでも標準関数にするには労力を必要とするのだろうがマクロを利用し自分の処理系を拡張していけば、それは言語自身に機能を追加していると同意なのかもしれない。ちょっと感覚が異なっている。処理系に機能を追加するって事の考え方がなんか違う。違和感を覚えるが、まあ〜そんな感じ。

そうか!言語の処理系を拡張するのがLISPはあまりにも容易なんで多数の処理系が乱立したのかな、と思った。

あとは「do」マクロがいまいちわからんね。凄く簡単な「do」の処理なら分かるんだが・・・

(do ((n 0 (1+ n))
     (cur 0 next)
     (next 1 (+ cur next)))
    ((= 10 n) cur))

いまいち理解しにくいよね。ん〜理解できるようにがんばる。「loop」は理解しやすかったが「loop」には賛否両論ある見たいだし、どうせならしっかりと「do」も理解したいよね。

と言うわけでがんばってみた。

(do ((nums nil) (i 1 (1+ i)))
    ((> i 10) (nreverse nums))
  (push i nums))

何やってんのか分からなかったよ。出力は「(1 2 3 4 5 6 7 8 9 10)」となる。予想はついたが正確に理解できてなかった。

1.「nums」に「nil」を束縛し、同時に「i」に「1」を束縛している
2.ループの終了条件テスト「(> i 10)」を実行する
3.終了条件となっていれば(iが10より大きければ)「(nreverse nums)」を実行し終了
4.本体の処理は「(push i nums)」を行う

3と4の間が微妙だが、4の「(push i nums)」は3での終了条件「(> i 10)」に当てはまらなかったら実行する処理ではなく、本体の処理ってところがミソかな?まあ〜ここまでは理解できたんだが、再度1に戻った場合、「nums」が「nil」で初期化されてしまわないか?「i」も「1」で初期化されないかと思って、考えに考えてどうしてもおかしいだろと悩んだが「do」の書式をもう一度確認。

(do (variable-definition*)
    (end-test-from result-from*)
  statement*)

でね「(variable-definition)」に関しては、ループ本体で使う事になる変数の導入。その書式は、

  (var init-from step-from)

という事は、

(do ((nums nil) (i 1 (1+ i)))
    ((> i 10) (nreverse nums))
  (push i nums))

の「(nums nil)」は変数「nums」に初期値「nil」を束縛し、ステップ数はなし、「(i 1 (1+ 1))」は変数「i」に初期値「1」を束縛しステップ数は「(1+ i)」という事。「variable-definition*」の部分は、ループ本体で使う変数の初期値とステップ数を宣言するところなんだね。まあ、そう書いてあるんだが理解できなかったんだよ。

多少、「do」を理解できた気がする。