本記事は「徹底解説! \expandafter 活用術(キホン編)」の続きにあたります。「キホン編」では単発の \expandafter
の使い方を学びましたが、この「ホンキ編」では \expandafter
を自在に使いこなすのに不可欠な「\expandafter
の鎖」について解説します。めざせ \expandafter
マスター!
※「キホン編」で用いた表記や用語をこの記事でも踏襲します。
\expandafter の“鎖則”
「キホン編」の最後で「展開可能プリミティブの一覧」を載せましたが、その中には他ならぬ \expandafter
が含まれています。\expandafter
が展開可能なのはある意味当然で、なぜなら最初に挙げた「\expandafter
の定義」は「\expandafter
プリミティブの展開の規則」そのものだからです。
\expandafter で \expandafter する件
それでは、\expandafter
の定義の B の位置に \expandafter
がある場合、つまり“\expandafter
A \expandafter
”を展開するとどうなるか、考えてみましょう。\expandafter
がある場合、その先に最低2つのトークンがあるはずですので、次の形を考えます。
\expandafter
A1\expandafter
A2 B…
ただし B… ⇒ B'… とする
「定義」の中の“B…”に相当する部分がここでは \expandafter
A2 B… なので、いつもの図式で考えると以下のようになるでしょう。
\expandafter
A1\expandafter
A2 B…
[\expandafter
A2 B… ⇒ ???]
⇒ A1 ???
??? の部分は何でしょう。これは \expandafter
の単純な展開です。
\expandafter
A2 B…
[B… ⇒ B'…]
⇒ A2 B'…
この結果をそのまま当てはめると、以下のようになります。
\expandafter
A1\expandafter
A2 B…
[\expandafter
A2 B…
[B… ⇒ B'…]
⇒ A2 B'…]
⇒ A1 A2 B'…
つまり、A1 や A2 は変化せずに B が展開される、という結果になりました。
もっと \expandafter を \expandafter してみる
では、ここで B がまた \expandafter
だったらどうなるでしょうか。先と同様に考えると、結果は以下のようになります。
\expandafter
A1\expandafter
A2\expandafter
A3 B…
[\expandafter
A2\expandafter
A3 B…
[\expandafter
A3 B…
[B… ⇒ B'…]
⇒ A3 B'…]
⇒ A2 A3 B'…]
⇒ A1 A2 A3 B'…
最終的な結果だけ見ると、以下の式が成り立ちます。
-
\expandafter
A1\expandafter
A2 B… ⇒ A1 A2 B'… -
\expandafter
A1\expandafter
A2\expandafter
A3 B… ⇒ A1 A2 A3 B'…
素敵な表記のおやくそく
さて、もっと先に進みたいわけですが、そうすると、ただでも長い \expandafter
という制御綴が大量に並ぶことになって、煩わしいことこの上ありません。そこで次のような、チョット素敵な表記規則を設けましょう。
-
\expandafter
の略記として と書く。
この表記法を取り入れると、先の展開規則は次のように書けます。
- A1 A2 B… ⇒ A1 A2 B'…
- A1 A2 A3 B… ⇒ A1 A2 A3 B'…
チョット素敵になりましたね!1
“\expandfater の鎖”の法則
本題に戻りましょう。先ほど行った、「B を \expandafter
Ai B に置き換えて、全体の展開を考える」という操作は何度でも繰り返すことができます。この方法で \expandafter
を増やすことで次のような一連の規則を導き出せます。(興味がある人は実際に導出してみてください。)
- A1 A2 B… ⇒ A1 A2 B'…
- A1 A2 A3 B… ⇒ A1 A2 A3 B'…
- A1 A2 A3 A4 B… ⇒ A1 A2 A3 A4 B'…
- A1 A2 A3 A4 A5 B… ⇒ A1 A2 A3 A4 A5 B'…
これを一般化して2得られるのが、次に示す「\expandafter の鎖則」です。
B… ⇒ B'…
であるとき
A1 A2 …… An B… ⇒ A1 A2 …… An B'…
「\expandafter
が連なってできた鎖がトークン列に“絡まっている”」ように見えるため「鎖則(chain law)」の名前がついています。
単発の \expandafter
を使うと「先頭にある A」が展開される前に「その次にある B」を展開することができたのですが、「\expandafter
の鎖」を使うと「もっと後ろにある B」を先に展開することができるわけです。
\expandafter の鎖則のガイドライン
「\expandafter
のガイドライン」にならって、「どういう場合に \expandafter
の鎖を使うべきか」という観点で整理した「\expandafter の鎖則のガイドライン」を用意しました。
- A… B… のようなトークン列があり、このままでは A が実行される。
- しかし、A の実行の前に B… を展開したい。
- その場合、A… の部分にある全てのトークンの直前に
\expandafter
を置けばよい。
例題:マクロの内容に追加する話
【例題4】引数無しのマクロ(内容は可変)
\xParHook
が定義されている。% 例えば \def\xParHook{\scsnowman[muffler=red]\relax}
このとき、「
\xParHook
の内容の末尾に\xMyHook
というトークンを追加する」コードを書け。(上記の例の場合、
\xParHook
の一回展開が\scsnowman[muffler=red]\relax\xMyHook
となるべき。)
まずは「\xParHook
の後に \xMyHook
を追加したもので \xParHook
を再定義する」と考えて次のような“原型”を作ってみます。
% あくまでも原型
\def\xParHook{\xParHook\xMyHook}
もちろんこのままでは \xParHook
が無限ループになってしまいます。ここで必要なのは「\def
が実行される前に(後ろの)\xParHook
を展開する」ことです。この状況を「ガイドライン」を当てはめてみましょう。
-
\def
…\xParHook
… というトークン列があり、このままでは\def
が実行される。 - しかし、
\def
の実行の前に\xParHook
を展開したい。 - その場合、
\def
… の部分にある全てのトークンの直前に\expandafter
を置けばよい。
今の場合、「\def
… の部分」のトークン列とは「\def``\xParHook``\{
」です。従って、この部分に \expandafter
の鎖を絡ませればよいわけです。つまり以下のようになります。
\expandafter\def\expandafter\xParHook\expandafter{%
\xParHook\xMyHook}
例題:キレイキレイにする話
【例題5】制御綴の列を内容にもつマクロ
\xGarbageList
がある。% "消したい"制御綴を入れておく \def\xGarbageList{\rm\sf\tt\bf\it\sl\sc}
このとき、LaTeXの内部命令
\@tfor
を利用して「\xGarbageList
の内容に含まれる各々の制御綴の定義を消去する(未定義に戻す)」コードを書け。
先ほどと同様に、ますは原型となるコードを作って、それから \expandafter
を加えていきます。
\@tfor\x:=\xGarbageList\do{%
\let\x\@undefined}% \@undefined は未定義の制御綴
まずループの中ですが、このままでは \x
自体に代入されてしまいます。「let
の実行の前に \x
を展開したい」ので \let
の前に \expandafter
を置きます。
\expandafter\let\x\@undefined
次に \xGarbaseList
の展開についてですが、「\@tfor
の実行の前に \xGarbaseList
を展開したい」ということなので、ガイドラインに従うと、「\@tfor``\x``:``=
」の部分に \expandafter
の鎖を絡ませればよいわけです。
\expandafter\@tfor\expandafter\x\expandafter:\expandafter=\xGarbageList\do{%
\expandafter\let\x\@undefined}
\expandafter の鎖に潜む罠
「\expandafter
の鎖」は応用範囲がとても広くて便利なのですが、重大な欠点があります。それは「コード中の鎖が“絡んでいる”部分の可読性が著しく損なわれる」ということです。例えば、先ほどの例題の解答のコードを改めて見返してください。「\@tfor\x:=...\do
」というループ構造を示す外見が、ほとんど視認できなくなってしまっています。後で“絡んでいる”部分のコードを改修しようとしても、その作業は困難を極めることになるでしょう。
この場合、鎖が“絡んでいる”部分の「\@tfor\x:=
」を一度マクロにすると、単発の \expandafter
で済ませられます。
\def\xTforXIn{\@tfor\x:=}
\expandafter\xTforXIn\xGarbageList\do{%
\expandafter\let\x\@undefined}
まだまだ解り難さは残っていますが、少なくとも実際に実行されるコードが「見えて」いるため、将来の改修に対応することができるでしょう。
\expandafter の長連の規準
このように、「\expandafter
の長い鎖」の使用は厳に慎まれるべきです。私自身は以下のような「\expandafter の長連の規準」を推奨しているので参考にしてください。
-
\expandafter
の3重を超える鎖が発生した場合は、それを回避する策をホンキで考えよう。 -
\expandafter
の5重を超える鎖は絶対に絶対に絶対に回避しよう。
この規準はかなり厳しいのは確かですが、実際にこのくらいに「長連の回避」を考える機会が得られないと、「回避するコツ」がなかなか身に付かないものです。
\expandafter の“ベキ乗則”
これまでの話では、\expandafter
を(単発でも鎖でも)使うと、後ろにあるトークンが一回展開できる、ということでした。それでは、後ろにあるトークンを「何回も」展開したい場合はどうすればいいでしょうか。考えましょう。
後ろを2回展開したい話
\def\csB{\csBi}
\def\csBi{\csBii}
\def\csBii{\csBiii}
\def\csBiii{\csBiv}
% つまり \csB ⇒ \csBi ⇒ \csBii ⇒ \csBiii ⇒ \csBiv
例えば、こういう定義があったとして、
「
\csA``\csB
」の後ろにある\csB
を2回展開したい
(つまり「\csA``\csBii
」に変えたい)
という状況を考えます。どのように \expandafter
を置けばよいでしょうか。
「\expandafter
では1回しか展開できない」という原則は変えられないので、これを実現するには「展開自体を2回にする」必要があることは確かです。
【概念図】
\csA``\csB
…⓪
⇒\csA``\csBi
…①
⇒\csA``\csBii
…②
まずは「①⇒②になるように①に \expandafter
を加える」という問題を考えましょう。\csBi
の一回展開が \csBii
なので、これは単純な単発の \expandafter
で解決できます。
【①⇒②は完成】
\csA``\csB
…⓪
⇒\csA``\csBi
…①
[\csBi
⇒\csBii
]
⇒\csA``\csBii
…②
ここで⓪も①と同じ形になるように前に \expandafter
を置きました。この状態で「⓪⇒①になるように⓪に \expandafter
を加える」という問題を考えます。よく見ると、これは「\expandafter
の鎖のガイドライン」が当てはめられることに気づきます。
-
\csA``\csB
というトークン列があり、このままでは が実行(展開)される。 - しかし、 の実行の前に
\csB
(⇒\csBi
)を展開したい。 - その場合、
\csA
の部分にある全てのトークンの直前に を置けばよい。- つまり、 → 、
\csA
→\csA
と置き換える。 - 従って結果は
\csA
となる。
- つまり、 → 、
つまり、完成形は以下のようになります。結果的に、先頭に3個の を置けばよいことになります。
【2回展開の完成形】
\csA``csB
…⓪
[\csB
⇒\csBi
](鎖則)
⇒\csA``\csBi
…①
[\csBi
⇒\csBii
](単発)
⇒\csA``\csBii
…②
後ろを3回展開したい話
2回展開ができたので、次は3回展開を考えてみます。
「
\csA``\csB
」の後ろにある\csB
を3回展開したい
(つまり「\csA``\csBiii
」に変えたい)
3回展開なので、全体のトークン列も3回展開する必要があります。先ほどと同様に「後ろから順に」考えてみましょう。
【概念図】
\csA``\csB
…⓪
⇒\csA``\csBi
…①
⇒\csA``\csBii
…②
⇒\csA``\csBiii
…③
②⇒③に単発の \expandafter
を適用します。
【②⇒③は完成】
\csA``\csB
…⓪
⇒\csA``\csBi
…①
⇒\csA``\csBii
…②
[\csBii
⇒\csBiii
](単発)
⇒\csA``\csBiii
…③
①⇒②に \expandafter
の鎖を適用します。
【①⇒②⇒③は完成】
\csA``\csB
…⓪
⇒\csA``\csBi
…①
[\csBi
⇒\csBii
](鎖則)
⇒\csA``\csBii
…②
[\csBii
⇒\csBiii
](単発)
⇒\csA``\csBiii
…③
最後に⓪⇒①の部分ですが、これも鎖則が適用できる形になっていることが判るでしょう。つまり、 \csA
に \expandafter
の鎖(もう「の鎖」でいいよね)を絡ませればよいわけです。
ここで少し一般的に考えてみます。「 がn個並んだ後にトークン X がある」というトークン列に対して「 の鎖の絡ませる」(列に含まれる各々のトークンの前に を置く)とどうなるでしょうか。「 がn個」の部分は個数が倍に増えて「 が2n個」になり、さらに X が X に変わるので、結局 X の前に2n+1個の がある恰好になります。この結果を「\expandafter 倍増の規則」(いや「 倍増の規則」かな?)と呼ぶことにしましょう。
×n X
に の鎖を絡ませると
×(2n+1) X
になる3。
この規則に従うと、×3 \csA
に鎖を絡ませた結果は ×7 \csA
となります。従って、結果的に、先頭に7個の を置けばよいことになります。
【3回展開の完成図】
\csA``\csB
…⓪
[\csB
⇒\csBi
](鎖則)
⇒\csA``\csBi
…①
[\csBi
⇒\csBii
](鎖則)
⇒\csA``\csBii
…②
[\csBii
⇒\csBiii
](単発)
⇒\csA``\csBiii
…③
さらに一歩進めて、4回展開はどうなるでしょうか。これまでの手順と同様に考えると、結局 ×7 \csA
の部分に再度 の鎖を絡ませれば済むことが判るでしょう。そして「 倍増の規則」により、 の個数は 2×7+1 で15個に増えます。
【4回展開の場合】
\csA``\csB
⇒\csA``\csBi
⇒\csA``\csBii
⇒\csA``\csBiii
⇒\csA``\csBiv
ベキ乗の法則
ここで、今までの考察の結果を、一般的なトークン列に対する規則としてまとめてみましょう。
【1回展開】(\expandafter
の定義)
B… ⇒ B'…
であるとき
A B… ⇒ A B'…
【2回展開】
B… ⇒×2 B'…
であるとき4
×3 A B… ⇒×2 A B'…
【3回展開】
B… ⇒×3 B'…
であるとき
×7 A B… ⇒×3 A B'…
【4回展開】
B… ⇒×4 B'…
であるとき
×15 A B… ⇒×4 A B'…
これを見ると、先頭に置くべき の個数は
1 → 3 → 7 → 15 → …
のように増えています5。この数列の第n項の値は 2n−1 で求められます。ここから「n回展開」に関する一般的規則を導き出すことができます。
【n回展開】
B… ⇒×n B'…
であるとき
×(2n−1) A B… ⇒×n A B'…
\expandafter
の個数が2のベキに従って増えていく様子から、この規則は「\expandafter のベキ乗則(power law)」と呼ばれることがあります。
ベキ乗則よりも大事なこと
この“ベキ乗則”は理論的には非常に面白い結果なのですが、しかし私はベキ乗則は覚える必要はないと考えています。なぜかというと、ベキ乗則を単純に適用できる状況は実用上はそう多くはないと考えているからです。実際のTeX言語のプログラミングで「複数回展開する」状況はもっと多様です。
- 複数回展開したいトークンがずっと後ろにある(つまり鎖則と複合する場合)
- そもそも先に展開したいトークンが複数個ある
- しかも各々のトークンで必要な展開回数が異なる
従って、「複数回展開する」状況に対応できるようになるために必要なのは、ベキ乗則を定理として覚えることではなく「それを導出する方法」を習得することだといえます。具体的には、以下のような要素を身につけることが必要です。
- 鎖則を自由に使いこなす
- 展開過程を「後ろから順に」構築する
- 「
\expandafter
倍増の規則」
そして、展開制御を上手に行う上で大事なコツは
そもそも**「後ろのトークンを何度も展開する状況」を作らない**
ということです。ベキ乗則から判るように、後ろのトークンを複数回展開しようと試みると、\expandafter
が文字通り指数爆発してしまいます。結果的に、それほど複雑でない状況であってもコードが「\expandafter
まみれ」になって全く読めなくなる、という悲惨な状況が簡単に発生します。
\expandafter
の高度な活用法を学習する際には、ぜひとも、他の展開制御の手法(\edef
による完全展開、など)も一緒に習得して、「\expandafter
が爆発しないように上手く展開制御する」ことを心がけましょう。
めざせ展開制御マスター!
練習問題(ホンキ編)
※ここの練習問題において、カテゴリコードの設定はLaTeXの \makeatletter
の状態を仮定します。また、my@
で始まる名前の制御綴(例えば \my@val
)は未定義であり自由に使ってよいものとします。
問題6: \expandafter の群れを展開する話
次のようなマクロが定義されているとする。
\def\gobble#1{}
\def\twice#1{#1#1}
この時、次のトークン列を一回展開した結果はどうなるか。
\expandafter\expandafter\expandafter\twice\expandafter
\twice\expandafter{\gobble\expandafter}{\gobble}
問題7: 名前で \let する話
etoolbox パッケージでは、\let
について「制御綴の代わりにその名前を指定する」変種として以下の命令を提供している。
-
\cslet{<名前A>}\制御綴B
:\制御綴B
を「名前が<名前A>
の制御綴」にコピーする。 -
\letcs\制御綴A{<名前B>} : 「名前が
<名前B>の制御綴」を
\制御綴A` にコピーする。 -
\csletcs{<名前A>}{<名前B>} : 「名前が
<名前B>` の制御綴」を「名前が `<名前A>` の制御綴」にコピーする。
これらの命令を自分で実装せよ。ただし制御綴の名前の引数は「完全展開すると文字トークンの列になる」ことを仮定してよい。
% 以下の4つの文は全て等価になるべき
\let\foo\bar
\cslet{foo}\bar
\letcs\foo{bar}
\csletcs{foo}{bar}
問題8: マクロの前後にナニカを追加する話
次の機能をもつマクロ \enclose
を実装せよ。
-
\enclose\制御綴A{<トークン列1>}{<トークン列2>}
: 引数無しのマクロ\制御綴A
について、その内容を、「前に<トークン列1>
、後ろに<トークン列2>
を追加したトークン列」に置き換える。-
\制御綴A
は引数無しのマクロであると仮定してよい。
-
以下に \enclose
の使用例を示す。
\def\xTest{\ARE\LaTeX}
\enclose\xTest{\ARE\TeX}{\ARE{TikZ}}
% \xTest ⇒ \ARE\TeX\ARE\LaTeX\ARE{TikZ}
まとめ
\expandafterと5回唱えたら願いが叶った。
— 露伴 (@Rohan_zzz) 2016年8月17日
皆さんも \expandafter
で幸せになりましょう!