マクロツイーター

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

暗黙的な波括弧は波括弧か波括弧でないか (1)

TeX の文法において、波括弧 { } は様々な場面で使用される。((厳密にいうと、「{ および }」でなくて「カテゴリコード 1 および 2 の文字」であるが、ここではカテゴリコード 1(および 2)をもつ唯一の文字が {})であるとする。))

  1. \hbox{残念だな! ここはもう\TeX の森の中だ} (ボックスの中身を示すトークン列)
  2. \toks@{な、なんだと…………!} (toks レジスタに代入するトークン列)
  3. \def\xx@macro{これは……} (マクロ定義の本体部)
  4. \textbf{えっ……!} (マクロ引数のグループ)
  5. {\Large うわああああぁぁあぁぁぁ!} (単純な局所化のためのグループ)

最後の 5 の意味は、TeX 文法の上で他に解釈がない場合に適用される。この場合の { ... }\begingroup ... \endgroup とほぼ等価である。ただし、内部では別の扱いになっていて、例えば、\tracinggroups を有効にして((e-TeX において \tracinggroups=1 を実行する。))グループのネストの状況を表示させると、{ ... } の方は「simple group」、\begingroup ... \endgroup の方は「semi simple group」と示される(日本語では、それぞれ「単純群」と「半単純群」と訳される……ような気がしない)。だから例えば \begingroup で始めた(局所化の)グループを } で閉じることはできない。また、数式中では、{ ... } は「サブ数式の導入」の意味になる((例えば「$3{+}5$」は「$3+5$」とは異なる組版結果になるが、「$3\begingroup+\endgroup5$」は「$3+5$」と同じである。))ことにも注意が必要である。

さて、ここでの本題は \bgroup\egroup というトークンについである。これは plain TeX(および LaTeX)で次のように定義されている。

\let\bgroup={ \let\egroup=}

これらのトークンは「暗黙的な波括弧(implicit braces)」と呼ばれる。これに対して、{ } 自体は「明示的な波括弧(explicit braces)」と呼ばれる。

\let で等置されているのだから、基本的には「暗黙」の方のトークンは「明示」の { } と同じ機能をもつはずである。例えば、(「他に解釈のない」箇所で)\bgroup\Large うわぁ\egroup を入力すると、simple group(つまり単純群*1)が生成されることになる。ところが、元の { } の用法によっては \bgroup ... \egroup では代替できない場合があるのである。例えば、マクロ引数の波括弧を暗黙的なものに変えてみる。

\textbf\bgroup えっ!\egroup

この場合、\bgroup は引数のグループの開始を意味しない。従って、\textbf の引数は単独の \bgroup ということとなり、これは当然エラーになる。マクロの引数のグループを表す波括弧は明示的でなければならないのである。

暗黙的な波括弧が使えるかはその用法によって異なり、また時には左右(開き側と閉じ側)で扱いが異なることもあるのでややこしい。しかし大抵の場合は、次のように考えれば判断がつく。

\bgroup{ と等しい「意味」をもつが { とは異なるトークンである。従って、「意味」が解釈される場面に限って、{\bgroup で代替できる。(}\egroup についても同様。)
1 の場合

\hbox から { までの間でトークンの「意味」は解釈される。((展開不能トークン(\relax 以外)が現れた場合は \hbox の文法に合っていないとそこでエラーになる。))従って、\bgroup を書けばそれは { の意味になる。そして、\hbox の中身の引数に入っても、トークンの「意味」は解釈される(展開の後に実行が行われて組版結果が生成される)。従って、ここで \egroup が現れればそれは } と解釈される。

2 の場合

\toks@ から { までの間の状況は 1 と同じ。\bgroup{ と解釈される。ところが、{ を読んだ後は、レジスタに代入するトークン列そのものの指定となるので、トークンが解釈されない状態になる。(展開も実行も行われない。)ここで \egroup を書くと、意味が解釈されないので } と違うものと見做され、引数の一部となる。

\toks@\bgroup ぐはっ!\egroup}
%→ } を読んだ時点で文が完成し、\toks@ に「ぐはっ!\egroup」が代入される
3 の場合

\def\xx@macro から { までの間はパラメタ指定であり、そこにあるトークンの意味は解釈されない。((例えば \let\foo=\relax としてもパラメタ区切り文字として \relax\foo は区別される。))ところが、{ を読んだ後は、マクロ本体の指定で、ここでもトークンは解釈されない。従って、この場合は、{ } はともに明示的でなければならない。

それでは、\def でなくて \edef の場合はどうか。結論を言うと、展開限定となる場合は明示的な波括弧しか認識されない。「展開限定の場合は展開不能トークンの意味は解釈されない」と理解しておけばよいだろう。この理屈で考えると、\write の場合は、開き側は暗黙でよいが閉じ側は明示のみ、となる。

\def\xx@macro\bgroup ぐはっ\egroup{}
%→ \bgroup も \egroup もパラメタ指定の一部となる。本体は後ろの {}
\def\xx@macro{ぐはっ\egroup}
%→ \egroup は本体の一部。} の箇所で終了
\edef\xx@macro{ぐはっ\egroup}
%→ 上と同じ
4 の場合

マクロの引数はマクロ定義のパラメタ指定とのマッチングで行われる。パラメタ指定のトークンは解釈されないので、マッチングでもトークンの意味は解釈されず同一のトークンにしかマッチしない。この事実を考えると、引数のグループは明示でなければならないことになる。(\bgroup はパラメタ指定の \bgroup とのみマッチするはずなので。)

5 の場合

{ が局所化のグループの開始になる条件では、\bgroup も同じう意味になる。グループの中に入ってもトークンの解釈は行われるので閉じ側も \egroup でよい。先述の \begingroup との区別の話と合わせると以下のようになる。

{うわぁ}                     % OK
\bgroup うわぁ\egroup        % OK
{うわぁ\egroup               % OK
\bgroup うわぁ}              % OK
{うわぁ\endgroup             % NG
\begingroup うわぁ\egroup    % NG
\begingroup うわぁ\endgroup  % OK

次回は、そもそも何故暗黙の波括弧で代替することが有意義であるかを述べる。

*1:違うってば。