マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

整数を読み飛ばす、続き (1)

2011-03-31 の記事の続き。

その記事では、任意の非負整数を「読み飛ばす」(空に展開する)方法を述べた。これを読んで「じゃあ負数を含めた任意の整数を読み飛ばすことはできないの?」と思った人がいるかも知れない。ここではそれを考えてみる。

これを行うには「展開可能なプリミティブで整数を引数にして結果何も出さない」ものが必要となるが、残念ながら TeX にそういうプリミティブは存在しないようだ。というかそんな命令は普通は何の役にも立たないからなくても当然だ。

ただ 1 つ可能性があるのは \if-トークンの利用である。\if-トークンは挙動が複雑で普通の「展開可能プリミティブ」とは訳が異なるが、展開可能であることは間違いない。すなわち


\edef\foo{\ifnum0=12345 \fi} % \foo の展開は空になる

を実行すると、12345 の部分が他の値であっても \foo の展開テキストは空になる。ということは、展開制御(\expandafter)を駆使して次の手順を踏めば良さそうである。

  • まず当該の「整数を表すトークン列」の前に「\ifnum0=」を置く。
  • その \ifnum を一度展開する。
  • その後に展開されるべきトークンとして \fi を割り込ませる。

残念ながら、この方法は次の理由で上手くいかない。

  • \ifnum の条件判断が偽だった場合、(実質的に)その時点で、「対応する」\fi(今の場合存在しないかも知れない)まで読み飛ばされてしまい、新たな \fi を割り込ませてもそれは先の \fi の後にしか来ない。

つまり、(現在 \ifx の有効な分岐の中だとして)


\ifnum0=100 \foo...\fi \bar

のように \ifnum0= を割り込ませて \ifnum を展開して直後に \fi を割り込ませても、その結果は


\ifnum0=100 \foo...\fi\fi \bar

と同値なもの(これでは \foo は処理されない)になってしまい、所望の


\ifnum0=100 \fi\foo...\fi \bar

\foo は処理されるべき)にならないのである。

ところで、上の例で判断が真(つまり 100 を 0 に変えた場合)は、そもそも読み飛ばしが発生しないため、所望の位置に \fi が入る。ということは、「整数を読んで、常に真となる条件文」があれば好都合である。しかし残念ながら TeX の条件文には該当するものがない。「最も安全(偽になる可能性が最も低い)」なのが、「\ifnum-"7FFFFFFF<〈n〉」でこれは n が -"7FFFFFFFTeX で扱える最小の整数値)でない限り成立する。TeX には ≦、≧ の比較がないのである。((「e-TeX だったら \unless\ifnum-"7FFFFFFF>〈n〉 で実現できる」と考えたかも知れない。確かにそうである。ただし、後で触れるように、e-TeX ではもっと安全な方法がある。))

取りあえず、「n = -"7FFFFFFF の時に破綻する」ことは諦めて、今の方法で、後続の任意の整数を読み飛ばすマクロ \xx@gobblenum を実装すると以下のようになる。


\def\xx@gobblenum{%
\expandafter\fi \ifnum-"7FFFFFFF<%
}

これで、(n ≠ -"7FFFFFFF の時)\xx@gobblenum〈n〉 は 3 回展開すると空になる。


\edef\xx@test{\xx@gobblenum 12345ABC} % \xx@test は「ABC」になる
\edef\xx@test{\xx@gobblenum\year\space ABC} % \xx@test は「 ABC」になる

実は、この \xx@gobblenum の定義は、もう 1 つ失敗する場合がある。次のように「該当の整数が数字表記でかつ終端に達しない内に \fi がある」場合、展開結果に何故か出所不明の \relax が出現する。


\edef\xx@test{\iftrue\xx@gobblenum 12345\fi X} % \xx@test は「\relax X」になる

これは TeX の仕様である。次のような場合が解りやすい。


\count9=1\iffalse2\else3\fi4 % \count9 は 134 になる
\edef\xx@test{\ifnum 22<11\else 3\fi X} % \xx@test は「\relax X」になる

上の行の例から解るように、数字表記の整数を読み込む場合、if 文自体は数字列を終結させない。ところが同じ理屈で下の行を処理しようとすると、「条件判断を決定するために 11 の次の文字を読む必要があるが、条件判断が決定しないため次の文字が判らない」というジレンマに陥ってしまう。従って、「条件部が終結しないまま \else\fi に達した」場合、TeX\else/\fi の前に \relax を自動挿入して「条件部を終結させる」処理を行う。展開結果に \relax が入るのはそのためである。

(続く)