マクロツイーター

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

付録の中にいるかを判定する件について

qa:56526 から始まる「付録の中にいるかの判定」についての話。

ある「解」が「正解」であるかは、当然「問題の立て方」に依存する。もし例えば

「article/report のそれと同じ意味をもつ \appendix 命令を有する」任意の文書クラスにおいて、「現在付録の中にあるのか(\appendix が呼ばれた後か)」を判定せよ

というのであれば、そもそも \@chapapp という内部マクロ自体が存在しない可能性があるので((article 系は「章(chapter)」が存在しないので、\@chapapp はないであろう。report/book 系に限ったとしても、\@chapappLaTeXカーネルlatex.ltx)でなく各文書クラスで定義されているものだから、結局存在を保証できない。))、このマクロを使って何かするという解法は全て不正解である。*1これに対して、例えば状況が次のように「限定」されているとする。

  1. \@chapapp マクロは文書クラス内で次のように定義される。
    \def\@chapapp{\chaptername}
  2. \appendix 命令は \@chapapp マクロを次のように再定義する。
    \def\@chapapp{\appendixname}
  3. \@chapapp の定義がこれ以外の方法で(パッケージやユーザにより)変更されることはないと仮定する。
  4. \chaptername\appendixname は再定義され得るが、両者が(1 回展開形において)同じにはならないと仮定する。*2

この仮定の下では、

\expandafter\ifx\@chapapp\appendixname

は「付録の中(\appendix の後)であるか」の判定として完全な「正解」となる。それは以下の理由による。

  • \expandafter を展開すると、(\@chapapp が展開されて)以下の形になる。
    \ifx〈\@chapapp の1回展開のトークン列〉\appendixname
  • \appendix の後 ⇒ 真」の理由: この場合、「\@chapapp の 1 回展開」は「\appendixname」なので、展開は「\ifx\appendixname\appendixname」となり、これは真と判定される。
  • \appendix の前 ⇒ 偽」の理由: この場合、「\@chapapp の 1 回展開」は「\chaptername」なので、展開は「\ifx\chaptername\appendixname」となる。仮定 4 より、この判定は確実に偽となる。((無引数マクロについて、1 回展開が異なれば必ず \ifx は偽となるが、1 回展開が同じでも真になるとは限らないことに注意。\long\outer といったマクロ属性があるためである。))

ところで、LaTeX 標準の report クラスにおいて、先の仮定の 1〜2 (3、4 は外的要因である)が成立するのかというと、実は微妙にそうなっていない。report.cls での \@chapapp の定義は

\newcommand{\@chapapp}{\chaptername}

であり、これは TeX 式に書くと以下と同じになる。

\long\def\@chapapp{\chaptername}

つまり、マクロに \long 属性がついている。しかし何故か、\appendix での再定義では \long なしの \def であり((実際は \gdef だけど、\global はマクロの属性ではないので無関係。))、再定義の前後で \@chapapp\long 属性が変わるという妙なことになっている(jsbook クラスでも全く同じ)。\long\ifx の判定に影響するので、このことは無視できない事実であるが、しかし少し考えると、\@chapapp 自体を \ifx の比較対象としてはいないので、結果的に同じ議論により、これらの場合でも上述の \ifx による比較が正当であることが示される。

さて、私からもこの件に関して 1 つ出題することにする。((ただし、私自身は、別にフラグ(スイッチ)を用意して、\appendix のところで立てる処理を行うのが適切だと考えているので、これは飽くまでも「問題のための問題」である。))

上述の「仮定」について、1〜3 は成立するが、4 は必ずしも成立しないという状況を仮定する。この「弱い仮定」の下で、\@chapapp の定義をみて(つまり \appendix の場所では他の処理を加えることなく)「\appendix の前か後か」を判定するにはどうすればよいか?

4 が成立せず \chaptername\appendixname が同じである場合は、\edef で完全に展開すると区別が付かなくなり失敗することに注意。


TeX 言語を学習している人の大半は何か他の言語を既に心得ているものだと思われるが、トークンの展開制御というものは TeX 特有の概念でそう簡単には身につかないのが普通だと思う。*3でも実は、展開制御をマスターしてしまうと、もはや TeX の実行制御での難所はクリアできたともいえる。「トークン列の展開のイメージ」が掴めれば、とにかく自分の書いたプログラムの実行制御が正しいかを判断することができる。勿論、TeX の「組版の機能」を習得するのはまた別の話になるが…。

*1:qa:56532 の最後で述べられているのもそういうことであろう。

*2:実際には、「両方とも空にする」という使用はありそうだが、とにかくそうでないとする。

*3:ちなみに「構造をもったトークン列上のマクロ」という概念は Lisp 系の言語にもある。「他の言語には関数しかないのに対して、Lisp 系言語には関数もマクロもあるから優れている」という主張がある。TeX にはマクロがあって関数がない。