これは「TeX & LaTeX Advent Caleandar 2016」の25日目の記事です。
(24日目は golden_lucky さん です。)
\expandafter is 何
\expandafter
はTeX言語のプリミティブの一種ですが、TeX言語のコードを書くときに限らず、(La)TeX者の日常生活における様々な場面で利用されています。
- お酒のネタにする(例)
- (特にTeX関連の)会議におけるノベルティグッズの意匠にする(例)
- 絵を描く際のモチーフにする(例1・例2)
- 独自の健康法のモチーフにする(例)
- 独自の体操のモチーフにする(例1・例2)
- とにかく連呼する(例1・例2)
- TeX言語のプログラムにおいて、展開制御のために書く
このうち、最初の6つに関しては、特に難しく考えることは何もありません。単に本能の赴くがままに \expandafter
の名を叫べばよいわけですから1。
これに対して、最後の「展開制御のために書く」についてはそう簡単にはいきません。TeX言語の学習が進んである程度複雑なプログラムを書く段階に至ると、「展開の順番を変えたい」と思って \expandafter
を試す人は多いようです。しかし、この「展開制御」というのはTeX言語特有の概念であるため、数多くのプログラム言語を制覇した強者であっても、マトモな理解を得るのはなかなか容易ではありません2。
世の中には \expandafter
の挙動を理解するための“チョット変わったアプローチ”を提唱している人もいます。しかし、この記事では特に奇をてらうことなく、\expandafter
の素朴な定義にホンキで取り組むことでその理解を目指します。
前書きの最後に、念のため、コレを書いておきましょう。
TeX言語注意!
Before \expandafter
そもそも「展開制御」が何かの役に立つためには「展開」についての理解が不可欠です。すなわち、TeX言語の「展開」の仕様、またその前提となる「字句解析(トークン化)」の仕様について十分に理解していない間は、\expamdafter
などの「展開制御」に手を出すべきでないのは当然でしょう。
そこで、まずは「\expandafter
以前」の理解確認のため、簡単なクイズをやってみましょう。
【クイズ1】次のTeXのソースを字句解析すると何個のトークン3からなると見なされるか。ただし、カテゴリコードの設定はLaTeXの通常通りであると仮定する。
\someproc A {B C\relax } {`\\}\nil %?
【クイズ2】次のコードが予め実行されていたとする。
\def\foo#1{foo}
この場合、次のトークン列を(先頭で)一回展開した結果のトークン列はどうなるか。
\csname\foo\foo\endcsname\foo\foo\relax
自信をもって答えを出せたならば合格です4。\expandafter
をホンキで理解したい人にとっては楽勝でしたね!
表記のおやくそく
展開制御は、字句解析された結果のトークン列に対して行われるものです5。そのため、トークン列を曖昧さなく示すために、本記事では次のような「トークン列の記法」を使うことにします。
- 制御綴6のトークンを
\csname
、文字トークンをA
のように等幅フォントで書きます。特に空白文字トークンを␣
と書きます。 - 見やすさのため、トークン列は
a
b
c
のように間を空けて書きます。間に空白文字トークンがあるわけではないことに注意してください。空白文字トークンは常に␣
で表されます。 - トークンを表す変数として A、B、… などの英大文字を使います。さらに、トークン列を A… のように表すことにします。
- 「トークン列 A… を一回展開すると B… になる」という言明を「A… ⇒ B…」と表記します。
例えば、“\someproc A {B C\relax } {`\\}\nil %?
”(先の問題に出てきたもの)を字句解析した結果のトークン列は次のようになります。
\someproc
A
␣
{
B
␣
C
\relax
}
␣
{
`
\\
}
\nil
※本記事では一貫して「LaTeX上のTeX言語プログラミング」を取り扱います。また、特に断りがない限り、「LaTeXの \makeatletter
の状態」のカテゴリコード設定を仮定します。
\expandafter のキホン
\expandafter、コワクナイヨー
一般に難解といわれる \expandafter
ですが、実はその定義は、次の示す通り、いたって単純なものです。
B… ⇒ B'…
であるとき
\expandafter
A B… ⇒ A B'…
これだけです。しかし、単純だからこそ、その確実な理解が大事なわけです。
\expandafter はなぜ動くのか
簡単な例で調べてみましょう。
\def\csAA{\csA\csA}
\def\csBB{\csB\csB}
このようにマクロが定義されているとします。この時、トークン列 \csAA
\csBB
を一回展開するとどうなるでしょうか?
\csAA
\csBB
⇒\csA
\csA
\csBB
これは当たり前ですね。ではこの先頭に \expandafter
を付けたものを考えましょう。これの一回展開はどうなるでしょうか?
\expandafter
\csAA
\csBB
定義に戻って考えましょう。今の場合、A に相当するのが \csAA
、B… に相当するのが \csBB
です。\expandafter
の定義に従うと、その展開結果を知るためには、B… の一回展開の結果である B'… を知る必要があります。
\csBB
(←これが B…)
⇒\csB
\csB
(←これが B'…)
\expandafter
の展開結果は A B'… ですから、結局 \csAA
\csB
\csB
となります。以上の結果を改めて整理してみましょう。
\expandafter
\csAA
\csBB
[\csBB
⇒\csB
\csB
]
⇒\csAA
\csB
\csB
結局 \expandafter
の追加により、A が展開される前に B が展開されたことになります。
\expandafter のガイドライン
以上の結果を「どういう場合に \expandafter
を使うべきか」という観点で改めて整理してみます。
- A B… のようなトークン列があり、このままでは A が実行(展開)される。
- しかし、A の実行(展開)の前に B… を展開したい。
- その場合、A B… の直前に
\expandafter
を置けばよい。
以下ではこれを「\expandafter のガイドライン」と呼ぶことにします。
例題:はじめての \expandafter
定義が解ったところで、さっそく \expandafter
を利用したコードを書いてみましょう。
【例題1】TeXのプログラム中に、以下のような
\let
文があった。% \xName に結果(\xResult)を格納する \let\xName\xResult
ところが、プログラムの改修で、letのコピー先の制御綴(
\xName
)を可変にする必要が生じた。このため、コピー先の制御綴を指定するためのマクロ\xTargetCs
を用意した。% 結果を格納する変数(マクロ名) \def\xTargetCs{\xName}%
このとき、「
\xTargetCs
の内容7の制御綴(上例の場合は\xName
)に\xResult
をコピー(\let
)する」というコードを書け。
もちろん以下のコードは正しくありません。
\let\xTargetCs\xResult % ダメ
これでは(\xTargetCs
の内容である)\xName
ではなく \xTargetCs
そのものが書き換えられてしまいます。この状況を先程の「\expandafter
のガイドライン」と照合してみましょう。
-
\let
\xTargetCs
というトークン列があり、このままでは\let
が先に実行される。 - しかし、
\let
の実行の前に\xTargetCs
を展開したい。 - その場合、
\let
\xTargetCs
の直前に\expandafter
を置けばよい。
ピタリと当てはまりました。つまり、次のようにすればよいわけです。
\expandafter\let\xTargetCs\xResult
期待通りに動くかどうか、シミュレートしてみましょう。
\expandafter
\let
\xTargetCs
\xResult
[\xTargetCs
⇒\xName
]
⇒\let
\xName
\xResult
大丈夫ですね。
例題:引数を完全展開したい話
\expandafter
の最もキホン的な使い方を心得たので、もう少し応用してみましょう。
【例題2】次のように、
\includegraphics
に渡すべき引数がマクロとして与えられている。\def\xImageOpt{\xImageSysOpt,\xImageUserOpt}% \includegraphics のオプション \def\xImageSysOpt{width=.8\linewidth} \def\xImageUserOpt{pagebox=artbox} \def\xImageFile{image-1.pdf}% \includegraphics の対象のファイル名
次のような形の
\includegraphics
文を実行したいが、引数がマクロのままでは正しく処理されない8。失敗% 引数のマクロを展開しないとダメ \includegraphics[\xImageOpt]{\xImageFile}
「引数(だけ)を完全展開してから
\includegraphics
を実行する」ようにするコードを書け。
引数の部分“[\xImageOpt]{\xImageFile}
”を完全展開したいわけなので、まずそこに \edef
を適用することを考えます。
※トークン列の完全展開(full expansion)を得るには \edef
が必要です。\expandafter
では対処できません。
% 引数の部分を完全展開する
\edef\xArgs{[\xImageOpt]{\xImageFile}}
% これで \xArgs ⇒ [width=.8\linewidth,pagebox=artbox]{image-1.pdf} となる
この \xArgs
を \includegraphics
文のしかるべき位置に置きます。
% \xArgs は"引数"である
\includegraphics\xArgs
ここで再び「\expandafter
のガイドライン」を思い起こしましょう。
-
\includegraphics
\xArgs
というトークン列があり、このままでは\includegraphics
が先に実行される。 - しかし、
\includegraphics
の実行の前に\xArgs
を展開したい。 - その場合、
\includegraphics
\xArgs
の直前に\expandafter
を置けばよい。
というわけで答えは次のようになります。(最初の \edef
から書いておきます。)
\edef\xArgs{[\xImageOpt]{\xImageFile}}
\expandafter\includegraphics\xArgs
この「引数を \edef
する → \expandafter
」のコンボは応用範囲が広いので、ぜひ身につけておきましょう。
補足:\expandafter しない方法
先ほどの例題2ですが、次のように \noexpand
を使っても解決できます。
\edef\xNext{\noexpand\includegraphics[\xImageOpt]{\xImageFile}}
\xNext
これは「次に実行すべき文の正しいトークン列を \edef
と展開抑止を組み合わせて作る」というパターンです。これも応用が効くので覚えておくといいでしょう。
チョット注意
トークンとはトークンである
ここでまたクイズです。次のようなコードを考えます。
\everypar\expandafter{\the\everypar\xSomething}\xAnother
これを実行すると、\everypar
の実行9の後に \expandafter
が展開されますが、この時に(定義における)A に相当する部分は何でしょうか?
\expandafter
の直後にあるトークンが A です。なので、この場合は {
(開き波括弧)となります。これが正解です。
特に難しい話ではないはずですが、ここでTeX言語初心者がよくやる間違いは、“A の部分”を「{\the\everypar\xSomething}
」だと考えてしまうことです。そもそも A は単一のトークンを表す変数であり、それが複数のトークンからなる列 {\the\everypar\xSomething}
を表すことは絶対にありえません。TeX言語の文法では {...}
で囲まれた「グループ」を一体のものとして扱う場面がよくありますが、この「グループ」自体は決してトークンではないことに注意してください。
ちなみに、今の場合の \expandafter
の展開過程は以下のようになります。ただしトークン列パラメタ \everypar
の現在の値を“\xFooBar
”とします10。
〈
\everypar
〉\expandafter
{
\the
\everypar
\xSomething
}
\xAnother
[\the
\everypar
⇒\xFooBar
]
⇒〈\everypar
〉{
\xFooBar
\xSomething
}
\xAnother
展開不能トークンで \expandafter する件
\expandafter
A B… の展開の際には B が展開されるわけですが、この時もし B が展開不能なトークンだったらどうなるでしょうか?
% この場合Bは'{'であり展開不能
\expandafter\begin{array}\xArgs
この場合は、便宜的に「B は B 自身に展開される」と解釈されます。
\expandafter
\begin
{
a
r
r
a
y
}
\xArgs
[{
⇒{
](と見なす)
⇒\begin
{
a
r
r
a
y
}
\xArgs
つまり、B が展開不能な場合は \expandafter
は全くの無駄、ということになります。
それでは、A が展開不能な場合の \expandafter
はどうでしょうか? 一見すると、こちらも同様に無駄であるようにも見えますが、実は違います。現に、最初の例題の正解のコードにおいて、A に相当するのは展開不能なプリミティブである \let
ですが、それでも \expandafter
は有意義でした。
% Aは'\let'であり展開不能
\expandafter\let\xTargetCs\xResult
この \expandafter
がなぜ意味をもつかというと、それは「一旦 \let
が実行されると、let文が完成するまで展開が抑止される」という性質があるからです。
[\expandafter
がない場合11]
\let
\xTargetCs
\xResult
→〈\let
〉\xTargetCs
\xResult
(let文開始)
→〈\let
\xTargetCs
〉\xResult
(展開されない)
[\expandafter
がある場合]
\expandafter
\let
\xTargetCs
\xResult
[xTargetCs
⇒\xName
](展開される)
⇒\let
\xName
\xResult
→〈\let
〉\xName
\xResult
(let文開始)
→〈\let
\xName
〉\xResult
このように、構文上で展開抑止が起こる箇所では「A が展開不能」であっても \expandafter
は必ずしも無駄にならないのです12。
プリミティブで \expandafter する件
「展開不能なトークン」に関してよくある誤解は「プリミティブは展開不能である」というものです。確かにプリミティブの多く(\let
等)は展開不能ですが、実際には展開可能なプリミティブ(\the
等)もあります。例えば先ほどの \everypar
の例では \the\everypar
⇒ \xFooBar
という展開を扱いました。
参考として、TeX言語の展開可能なプリミティブのうち重要なものを列挙しておきます。
- 制御綴構成:
\csname
- 値取得・文字列化:
\the
\string
\meaning
\number
\romannumeral
- 条件分岐: 各種ifトークン(
\ifnum
等)\else
\fi
- 展開制御:
\noexpand
\expandafter
これらは展開可能であるため、\expandafter
の定義における B の位置に来る場合があります。
例題:\csname で \expandafter する件
これらの展開可能プリミティブのうち、\expandafter
と絡んでよく使われるのが \csname
です。なので、これについて詳しく考えましょう。
【例題3】例題1では、コピー先の制御綴を可変にするために、その制御綴そのものを入れたマクロ
\xTargetCs
を用意した。\def\xTargetCs{\xName}
これを少し変えて、次のように“制御綴の名前13”を内容に持たせる仕様にしたい。
\def\xTargetCsName{xName}% コピー先の制御綴の*名前*
では「
\xTargetCsName
の内容の文字列を名前とする制御綴に\xResult
をコピー(\let
)する」というコードを書け。
\xTargetCsName
(の内容)の名前をもつ制御綴を作るために \csname\xTargetCsName\endcsname
としましょう。ここで問題なのは「これの一回展開はどうなるか」ということです。一回展開しただけで目的の制御綴(\xName
)になるのでしょうか?
答えはYesです。つまり \csname
~\endcsname
の一回展開は“~”の部分の名前をもつ単一の制御綴となります14。この際に“~”の部分は完全展開されるという規則になっています。
\csname
\xTargetCsName
\endcsname
[\xTargetCsName
―(完全展開)→x
N
a
m
e
]
⇒\xName
一回展開で十分なことが判れば、あとは簡単ですね。\let
の実行より先に \csname
を一回展開すればよいので以下のようになります。
\expandafter\let\csname\xTargetCsName\endcsname\xResult
\expandafter
\let
\csname
\xTargetCsName
\endcsname
\xResult
[\csname
\xTargetCsName
\endcsname
⇒\xName
]
⇒\let
\xName
\xResult
練習問題(キホン編)
以上で、\expandafter
の最も基本的な使い方(単発の \expandafter
)についての解説は終わりです。ここまでの内容をちゃんと理解できたかを確認するため、キホン的な練習問題に挑戦してみましょう。
※ここの練習問題において、カテゴリコードの設定はLaTeXの \makeatletter
の状態を仮定します。また、my@
で始まる名前の制御綴(例えば \my@val
)は未定義であり自由に使ってよいものとします。
問題1: \expandafter を展開する話
次のようなマクロが定義されているとする。
\def\gobble#1{}
\def\twice#1{#1#1}
この時、次のトークン列を一回展開した結果はどうなるか。
\expandafter\gobble\twice\gobble\twice\twice\gobble
問題2: \@namedef を自作する話
LaTeXには「制御綴の代わりに制御綴の名前を指定してマクロを定義する」ための \@namedef
という内部マクロが存在する。例えば、次の2つの文は等価な動作を行う。
% \@namedef{<名前>}<パラメタテキスト>{<置換テキスト>}
\@namedef{Hoge}#1#2{\message{#1 and #2}}
% ↑は↓と同等な動作をする
\def\Hoge#1#2{\message{#1 and #2}}
では、この機能をもつ \@namedef
を自分で実装せよ。
問題3: ドライブレターの有無を判定する話
次の機能をもつマクロ \hasdrivespec
を実装せよ。
-
\hasdrivespec{<文字列>}
: 引数のトークン列を完全展開して得られる文字列について、その2文字目が:
であるか(つまりWindowsのドライブレター付きの絶対パス名であるか)否かを判定し、その結果をスイッチ15\if@tempswa
に返す。- スイッチ
\if@tempswa
はLaTeXでは予め定義されている。 - 引数は「完全展開すると“普通の文字列”(カテゴリコード11か12の文字トークンの列)になる」ことを仮定してよい。
- スイッチ
以下に \hasdrivespec
の使用例を示す。
\def\xOneDir{C:/tmp/tex}
\def\xOneFile{advent.txt}
\def\xOnePath{\xOneDir/\xOneFile}
\hasdrivespec{D:/fonts}\if@tempswa Yes\else No\fi
%→ "Yes"と出力
\hasdrivespec{\xOneFile}\if@tempswa Yes\else No\fi
%→ "No"と出力
\hasdrivespec{\xOnePath}\if@tempswa Yes\else No\fi
%→ "Yes"と出力
問題4: 制御綴を得る話
次の機能をもつマクロ \makecs
を実装せよ。
-
\makecs\制御綴A{<名前>}
:\制御綴A
を、内容が「名前が<名前>
である制御綴」であるマクロとして定義する。- 引数は「完全展開すると文字トークンの列になる」ことを仮定してよい。
以下に \makecs
の使用例を示す。
\makecs\xTest{space}
% \xTest ⇒ \space となる
\def\&{and}
\makecs\xTest{exp\&after}
% \xTest ⇒ \expandafter となる
問題5: 一回展開を調べる話
e-TeX拡張をもつTeXエンジン16は \showtokens
というプリミティブを持つ。\showtokens{<トークン列>}
を実行すると、\show
と同様の情報表示の様式で、引数のトークン列が(展開されずに)端末にそのまま表示される。
*\showtokens{\foo\bar\expandafter} %←'*'はプロンプト
> \foo \bar \expandafter . %←引数がそのまま出る
<*> \showtokens{\foo\bar\expandafter}
? %←入力待ちになる
では、この \showtokens
プリミティブを用いて「与えられたトークン列の一回展開がどうなるか」を調べる手順を構成せよ。そしてその手順を利用して問題1に対する自分の解答が正しいことを確認せよ。
まとめ
\expandafrer
は単なるTeXのプリミティブです。コワくありません!
「\expandafter のガイドライン」を活用して思う存分 \expandafter
しましょう!
- A B… のようなトークン列があり、このままでは A が実行(展開)される。
- しかし、A の実行(展開)の前に B… を展開したい。
- その場合、A B… の直前に
\expandafter
を置けばよい。
\expandafter
のホンキが見たい人はこちらへ。
-
\expandafter
の読み方について特に決まりごとはありません。「エクスパンドアフター」でも「エキスパンドアフター」でも「エークスペァァァーンデァーフトゥゥ!!!!!」でも好きなように叫んでください。 ↩ -
下手に自分の知っているプログラミング言語の概念で“近似”して先に急いで進もうとすると、本来の定義とのズレのために後々になって頭を抱えることになりかねません。 ↩
-
本記事では、TeXの用語の“token”の訳語に「トークン」を用いることにします。 ↩
-
チョット気になる人のために正解を書いておきます。【クイズ1】15個です。【クイズ2】「
\foo\foo\foo\relax
」となります。 ↩ -
もちろん、実際のTeX処理系においては字句解析と展開は同時進行で処理されます(参考記事)。それでも、展開に関する議論を行う場合は、“字句解析に関するヤヤコシイ話”が絡むのを避けるために、敢えて“字句解析が済んだ後のトークン列”を前提にする方が適切でしょう。(特に、展開だけが行われている状況では、字句解析に影響を与える“カテゴリコードの変更”が起こらないことに注意しましょう。) ↩
-
本記事では、TeXの用語の“control sequence”の訳語を「制御綴」とします。他文献では「コントロール・シーケンス」と呼ばれることもあります。 ↩
-
この記事では、引数無しのマクロについて、一回展開した結果のトークン列のことを便宜的に「内容」と呼ぶことにします。 ↩
-
一般的に、key-value形式の引数について、“key=value”全体がマクロになっているとそれは正常にパーズされません。 ↩
-
\everypar
トークンの実行により、「\everypar
パラメタに対する代入文」が開始されます。 ↩ -
先頭にあった
\everypar
は\expandafter
の展開時には既に“実行されてしまっている”ため入力バッファ上にはありません。(このため図では〈 〉に入れて示しました。)この後}
まで読んだところで、“\everypar
の代入文”として「\everypar{\xFooBar\xSomething}
」が完成することになります。 ↩ -
ここの図の中の“→”は(“⇒”とは異なり)「実行の1ステップ」を表します。また〈 〉は「既に実行されてバッファ上にないトークン」を表します。「実行」とは何か、についてはあまり深く考えずに直感的に把握しましょう。(えっ) ↩
-
もう一つ例を挙げます。先の「
\everypar
の代入文」のケース(everypar\expandafter{...}
)では、トークン列パラメタの代入文の文法規則として、{
を実行してから}
を読むまでの間に展開抑止が起こります。従って、(展開不能な){
の前の\expandafter
が意味をもつわけです。 ↩ -
TeX言語において、制御綴
\foo
の名前とは、先頭のエスケープ文字(\
)を除いた“foo
”のことを指します。ちなみに、LaTeXにおいて「命令の名前」という場合はエスケープ文字を含めた“\foo
”を指すのが一般的です。 ↩ -
結果の制御綴はそれ以上展開されません。つまり、今の場合、
\xName
が展開可能であったとしても、\csname
… の一回展開は飽くまで\xName
となります。 ↩ -
\newif
により作成される\if
-トークンのことをスイッチ(switch)といいます。 ↩ -
今時のLaTeXは全て、e-TeX拡張をもつエンジンの上で動作しています。 ↩