マクロツイーター

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

きょうの e-TeX (1):\ifdefined、\ifcsname

なんとなく、e-TeX拡張機能の解説をするシリーズを始めていた。例によって、いつまで続くかは不明。初回はこの 2 つ。((なお、冒頭の紹介文での説明は LaTeX と非依存の形で行うが、その後の解説文の中では一般的に LaTeX\makeatletter の状態を前提とする。))

  • \ifdefined〈トークン〉 [if-トークン] : 〈トークン〉が定義済であるかを判定する。
  • \ifcsname〈文字列〉\endcsname [if-トークン] : 「\〈文字列〉」という制御綴が定義済であるかを判定する。\csname のように、該当の制御綴を \relax にすることはない。

このうち、\ifdefined〈トークン〉 の方は、非 e-TeX でも \ifx〈トークン〉\@undefined((\@undefinedLaTeX で「未定義である」ことが約束されたトークン。))の逆条件で完全に代用できる。しかし、\ifcsname は非 e-TeX で完全に代替することはできない拡張機能である。

非標準な(カテゴリコード 11 以外の文字を含む、可変部分を含む、等)制御綴が定義されているかを調べたい場合、例えば \[xx@foo/\xx@id]((「xxx」という文字列からなる制御綴りを \[xxx] と記述することにする。例えば、\csname 1+1=2\endcsname の展開結果が \[1+1=2] である。))(\xx@id は文字列に展開されるマクロ)が定義済かを調べたい場合を考える。\ifcsname を用いて次のように実現できる:


\ifcsname xx@foo/\xx@id\endcsname 〈真〉 \else 〈偽〉 \fi

しかしこういう処理は e-TeX 登場以前の TeX でのプログラミングでも度々用いられる。非 e-TeX では通常次のような構文を用いる:


\expandafter\ifx\csname xx@foo/\xx@id\endcsname\relax 〈真〉 \else 〈偽〉 \fi

ところが、この構文には「\[xx@foo/\xx@id] が未定義の場合と \relax と等価な場合の両方に真と判定される」、および「\[xx@foo/\xx@id] が未定義の場合は \relax と等価に定義(\let)されるという副作用がある」という難点がある(これは \csname がそういう副作用をもつからである)。そして、そういう「周知の癖」があるため、TeX プログラマは「非標準な制御綴については、未定義であることと \relax であることは区別しない」という習慣に従ってきた。

ただ、何か理由があって、「非標準な制御綴について未定義と \relax を区別したい」場合もある。そういう場合には \ifcsname が非常に有効であると考えられる。

ただし、実は今の場合にも次のような非 e-TeX での対処法が知られている:


\begingroup\expandafter\endgroup
\expandafter\ifx\csname xx@foo/\xx@id\endcsname\@undefined

\expandafter の原理」に従うと、これは「\begingroup\endgroup\ifx\[xx@foo/\xx@id]\@undefined」と等価になることが解るであろう。ここで、\csname 展開時に制御綴の \relax への定義は行われているが、それはグループの中であり、直後にそのグループを抜けているので、結局定義はなかったかのように振舞う。

ただこれも \ifcsname を使った判断(の逆条件)と全く等価ではない。なぜなら、「余計な \begingroup\endgroup」があるからである。このことは「展開のみ行われる状況」(\edef の置換テキストの中など)では本質的な違いを生む。\ifcsname は展開可能であり、それ故に完全展開可能性が要求されるマクロの中で使われる可能性があり、このような場合は先の構文で代替することはできないのである。

ちなみに、次のようにすると、「制御綴が未定義と \relax の両方で真と判断されるが、\relax への定義は行われない」という処理になる:


\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname xx@foo/\xx@id\endcsname\relax

これの動作原理を理解するのは最初の構文よりも難しい。\expandafter と条件分岐が絡むからである。

非標準な制御綴の定義済を検査するのに専ら \ifcsname を用いることにすれば、先述の「未定義と \relax は区別しない」という習慣を捨てることができる。ただ、\csname の副作用は当然 e-TeX でも残ったままなので、その点に注意が必要である。