[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
単純なテキストに展開されるようなマクロだけでは、 引数を取ることができるとしても、十分ではありません。 実行時に下される判断にもとづいて、 異なる展開がおこなわれるようなマクロが必要でしょう。 たとえば、何らかの条件構文が必要です。 また、ある処理を何回も繰り返したり、条件が真の間だけ繰り返したりするために、 ある種のループ構文も必要でしょう。
5.1 マクロが定義済みかを判定する | ||
5.2 文字列の比較 | If-else 構文と多重分岐 | |
5.3 ループと再帰 | m4におけるループと再帰 |
m4
には2つの異なる条件構文が組み込まれています。
その1つはifdef
です。
ifdef(name, string-1, opt string-2) |
これにより、あるマクロが定義されているかどうかをテストできるようになります。
nameがマクロとして定義されていれば
ifdef
はstring-1に展開され、
そうでないときはstring-2に展開されます。
string-2が省略されたときは
(通常の規則に従い)空文字列として解釈されます。
ifdef(`foo', ``foo' is defined', ``foo' is not defined') ⇒foo is not defined define(`foo', `') ⇒ ifdef(`foo', ``foo' is defined', ``foo' is not defined') ⇒foo is defined |
マクロifdef
は引数を与えたときだけ認識されます。
もう一方の条件構文ifelse
はずっと強力です。
与える引数の個数によって長いコメントの挿入のためや、
if-else構文、多重分岐などとして使うことができます。
ifelse(comment) ifelse(string-1, string-2, equal, opt not-equal) ifelse(string-1, string-2, equal, ...) |
ifelse
に1つだけ引数を与えた場合、
それは単に捨てられて、何も出力されません。
これはdnl
を何度も使わずにブロックコメントを
挿入するために良く使われる、m4
におけるイディオムです。
GNU m4
ではこの特殊な使用法が認められているので、
引数が足りないことに対する警告はこのケースでは発せられません。
ifelse
に3つ、または4つの引数を与えて呼び出すと、
string-1とstring-2が(文字毎に比べて)等しければ
equalに展開されます。
等しくなければnot-equalに展開されます。
ifelse(foo, bar, `true') ⇒ ifelse(foo, foo, `true') ⇒true ifelse(foo, bar, `true', `false') ⇒false ifelse(foo, foo, `true', `false') ⇒true |
またifelse
には4つ以上の引数を与えることができます。
この場合、ifelse
は伝統的なプログラミング言語におけるcase
文や
switch
文と同じように機能します。
string-1とstring-2が等しければifelse
はequalに
展開され、等しくなければ最初の3つの引数が捨てられたあと、
まったくおなじ手続きが繰り返されます。例で示したほうがいいでしょう。
ifelse(foo, bar, `third', gnu, gnats, `sixth', `seventh') ⇒seventh |
もちろん、通常はこれらの例よりもうすこし高度な使い方をするでしょう。
ifelse
のよくある使い方のひとつは、さまざまな種類のループ処理を
実装するマクロの中で使用する場合です。
マクロifelse
は引数を与えたときだけ認識されます。
m4
ではループ処理が直接的にはサポートされていませんが、
再帰的なマクロを定義することはできます。
使用しているハードウェアとオペレーティングシステムによるもの以外、
再帰の深さに制限はありません。
ループ処理は再帰とすでに説明した条件構文を使うことで実現できます。
マクロの実引数を反復処理するときには組み込みマクロshift
を
使うことができます。
shift(...) |
このマクロは任意の個数の引数を受け取り、 最初の引数を除く残りの引数をそれぞれクォートしてから、 それらをコンマで区切ったものに展開します。
shift(bar) ⇒ shift(foo, bar, baz) ⇒bar,baz |
shift
を使った次の例では引数の順番を逆にするマクロを定義しています。
define(`reverse', `ifelse($#, 0, , $#, 1, ``$1'', `reverse(shift($@)), `$1'')') ⇒ reverse ⇒ reverse(foo) ⇒foo reverse(foo, bar, gnats, and gnus) ⇒and gnus, gnats, bar, foo |
それほど興味深いマクロではありませんが、
shift
とifelse
そして再帰を使えばループ処理をどんなに簡単に
実現できるか示しています。
次に挙げるのは、単純なforループを実現するマクロの例です。 単純な数え上げのためなどに使えます。
forloop(`i', 1, 8, `i ') ⇒1 2 3 4 5 6 7 8 |
それぞれの引数は順に、反復変数(iteration variable)の名前、
開始値、終了値、そして反復するたびに展開されるテキストです。
このマクロにおいて、マクロi
はループ処理の内部でだけ定義されています。
i
が以前に値を持っていた場合は、ループが終ればまたその値にもどります。
forloop
は次のように入れ子にすることもできます。
forloop(`i', 1, 4, `forloop(`j', 1, 8, `(i, j) ') ') ⇒(1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (1, 6) (1, 7) (1, 8) ⇒(2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (2, 7) (2, 8) ⇒(3, 1) (3, 2) (3, 3) (3, 4) (3, 5) (3, 6) (3, 7) (3, 8) ⇒(4, 1) (4, 2) (4, 3) (4, 4) (4, 5) (4, 6) (4, 7) (4, 8) ⇒ |
forloop
マクロはとても簡潔に実装することができます。
forloop
マクロ自体は単なるラッパー(wrapper)で、
第1引数が持つ元の定義を保存してから、内部マクロ_forloop
を呼び、
再び保存しておいた第1引数の定義を再確立します。
マクロ_forloop
は第4引数を一度展開し、これで反復が終りかどうか調べます。
もし終りでなければ、反復変数を(すでに定義済みのマクロincr
を使って)
1増やしてから、自分自身を再帰的に呼び出します。
forloop
の実際の実装は次のようになります:
define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')') define(`_forloop', `$4`'ifelse($1, `$3', , `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')') |
注意深い引用符の使い方に注目してください。 マクロの引数でクォートされていないのは3つだけで、 それぞれに固有の理由があります。これら3つの引数がクォート されていないのはなぜか、その理由を見つけてください、 これらがクォートされていると、どうなるのかも確かめてみてください。
これら2つのマクロは便利ではありますが、 一般の使用に耐えるほど堅牢ではありません。 開始値が終了値より小さい場合や、第1引数が名前でなかった場合など、 初歩的なエラー対策さえ欠いています。 これら不備の訂正は、読者への課題として残しておきます。
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Akihiro Sagawa on June, 15 2005 using texi2html 1.70.