全5515文字
PR

 Schemeのifでは「条件」「条件が成り立つ場合の処理」「条件が成り立たない場合の処理」の3つを順番に記述する。条件が成り立たない場合の処理は省略可能だ。他の言語のifとほぼ同じと考えていい。

(define a 3)
(if (> a 0)
    (display "greater than 0")
    (display "0 or less than 0"))

 ここでは、aが0よりも大きければ「greater than 0」、そうでなければ「0 or less than 0」を表示するようにしている。aとして3を定義しているので、実行すると「greater than 0」が表示される。

 Schemeでは「#f」が偽を表し、「#t(および#f以外のすべての値)」が真を表す。試しに「(= 0 1)」を実行してみると、この式は誤りなので「#f」になるのを確認できる。

 一方、C言語では「0」が偽を表し、「0以外」が真を表す。そこで、C言語のように0かどうかで真偽判定を行う「c-like-if」をSchemeで実装してみよう。

 まず、c-like-ifを関数として実装する方法を試してみる。test、then、elseの3つの引数を取ることにしよう。testが0でなければthenを実行し、それ以外、つまり0の場合はelseを実行するよう定義した。ちなみにSchemeにはノットイコールを表す比較演算子はないので、否定を表す「not」を「=」に組み合わせている。notを使う代わりにthenとelseの位置をひっくり返してもかまわない。

(define (c-like-if test then else)
  (if (not(= test 0))
      then
      else))

(c-like-if 0
  (display "true\n")
  (display "false\n"))

 このプログラムでは条件として0を与えているので、「false」だけが表示されると思うかもしれない。しかし実際に実行してみると、「true」「false」の両方が表示されてしまう。これでは条件分岐の役割を果たせない。

 なぜこんなことが起こってしまうのだろうか。原因はc-like-ifを関数として定義したことにある。関数は、引数を受け取った段階でその引数の式を計算してしまう。このため、thenとして「(display ”true¥n”)」、elseとして「(display ”false¥n”)」を受け取った段階で、それぞれのメッセージを表示してしまうのだ。

 この問題を解決するにはマクロを使う必要がある。マクロは関数とは異なり、受け取った引数をすぐには計算しない。

 具体的には、Schemeのマクロは次のように記述する。定義には「define-syntax」というキーワードを使う。「syntax-rules」という記述が追加され、引数を記述する場所も変わっている。

(define-syntax c-like-if
  (syntax-rules ()
    ((c-lile-if test then else)
       (if (not(= test 0))
           then
           else))))

(c-like-if 0
  (display "true\n")
  (display "false\n"))

 このプログラムを実行すると「false」だけが表示される。ifを拡張するには、関数ではなくマクロが必要なことが分かっただろう。

 このように、マクロを使うと関数では表現できない新たな構文を生み出せる。言語の文法自体を改変できるのだ。

 この記事で紹介したのはLispのほんのさわりにすぎない。しかしこれだけでも柔軟で強力な言語であることは十分に感じられたと思う。Lispの魅力にとりつかれ、抜け出せなくなる人は多い。Lisp沼へようこそ。