Lispの文法は知ってるけど大きなプログラムは書いたことがなくて、また使っているイディオムも他の言語で書くときのような範囲でしか触っていない。On Lispによれば、「マクロはプログラムを書くプログラムだ」(Macros are programs that write programs)と言われる。なんかそういう、Lispならではということをしたい。
Lispでは、ペアの頭を返すcar
や尻尾を返すcdr
という基本的な関数があって、またそれらを組み合わせたcadar
とかcddddr
とかいう関数がある。こういうのを自動的に作り出してみよう。
古典的マクロを使って、
(define-macro (gen-cxxr cxxr) |
これを使って
(gen-cxxr cadr) ;=> (define (cadr x) (car (cdr x))) |
と書けば、決まった法則で関数を生成できる。
これでもいいっちゃーいいんだけど、(gen-cxxr ...)
とずらずら書くのがダルい。そこで複数のシンボルを定義するマクロを作ってやれば:
(define-macro (gen-cxxrs . cxxrs) |
ということができる。
ただ、機能的には他の言語でもできないこともなくて、例えばRubyで配列をペアに見立てて、car
とcdr
というメソッドを定義しておいて:
class Array |
gen_cxxr
を:
# Ruby版A |
または:
# Ruby版B |
みたいにすればできる。ただし動作は異なっている。Lispのマクロの場合には関数の生成はコンパイル時に行われて、実行時には通常の関数呼び出しと全く同じになる。
Ruby版Aは実行時にinject
~`send`によるメソッド呼び出しのオーバーヘッドが必要になっている。
Ruby版Bは、Rubyをコンパイルと実行の2段階に分けて考えるとLisp版と同じ動作だけど、明示的なeval
が必要になっている。またメソッドの内容は文字列で構築しないといけないので、複雑な操作は難しい。
…なんか最初の目的とずれてるような気がするが、終わりです。
- cxxxxr の定義を作る - Higepon’s blog これはコンパイル時じゃなくて、プリプロセス的に文字列としてSchemeのソースを生成しているようだ