expl3 における「展開制御支援」
このように完全展開の概念を 2 つに分ける理由は、expl3 の言語のもつ「展開制御支援」の機能にある。
1 つのトークン列を引数にとる関数(マクロ)があったとする。命名規則に従えば、そのような関数の引数指定子(argument specifier)は「n」となるので、関数名は :n
で終わるはずである。いま関数名を \xx_some_fun:n
とする。この時、標準モジュールの関数 \exp_args:〜
を用いて次のようなことができる。
\exp_args:Nx \xx_some_fun:n {<引数>}
: 引数を網羅展開してできるトークン列を\xx_some_fun:n
に渡す。\exp_args:Nf \xx_some_fun:n {<引数>}
: 引数を完全展開してできるトークン列を\xx_some_fun:n
に渡す。
例を挙げて説明する。標準関数の \tl_show:n{<引数>}
は、\show のように一旦停止して引数のトークンの内容をそのまま画面に出力する。(正体は e-TeX の \showtokens
プリミティブ。)例えば
\tl_show:n { A B \prg_do_nothing: { C } \scan_stop: }
を実行すると、画面に以下のように表示される。(expl3 の環境ではソース中の空白文字は無視されることに注意。)
> AB\prg_do_nothing: {C}\scan_stop: . <*> ... { A B \prg_do_nothing: { C } \scan_stop: } ?
それでは、この \tl_show:n
を使って、\exp_args:〜
の実験をしてみよう。
\documentclass{article} \usepackage{expl3} \ExplSyntaxOn % トークン列変数 \zrxx_foo_tl、\zrxx_bar_tl を用意し、 % \zrxx_foo_tl を 2 回展開すると文字列「FOO」、 % \zrxx_bar_tl を 1 回展開すると文字列「BAR」に % 展開されるようにする。 \tl_new:N \zrxx_foo_tl \tl_new:N \zrxx_foo_i_tl \tl_new:N \zrxx_bar_tl \tl_set:Nn \zrxx_foo_tl { \zrxx_foo_i_tl } \tl_set:Nn \zrxx_foo_i_tl { FOO } \tl_set:Nn \zrxx_bar_tl { BAR } % 単純に \tl_show:n を呼び出す。何も展開されないはず。 \tl_show:n { \zrxx_foo_tl / \zrxx_bar_tl } % \exp_args:Nf を付ける。完全展開されるはず。 \exp_args:Nf \tl_show:n { \zrxx_foo_tl / \zrxx_bar_tl } % \exp_args:Nx を付ける。網羅展開されるはず。 \exp_args:Nx \tl_show:n { \zrxx_foo_tl / \zrxx_bar_tl } \ExplSyntaxOff \begin{document}% もはや本文は無意味 \end{document}
これを latex でコンパイルすると、画面に以下のように表示される。
> \zrxx_foo_tl /\zrxx_bar_tl . % 展開されない …(中略)… ? % Enter空打ち > FOO/\zrxx_bar_tl . % 完全展開形 …(中略)… ? % Enter空打ち > FOO/BAR. % 網羅展開形 …(中略)… ? x % x で終了
実は、ある関数の展開型を変えるために、\cs_generate_variant:Nn
というもっと便利なものがあって、
\cs_generate_variant:Nn \tl_show:n { f } %→ \tl_show:f が「\exp_args:Nf \tl_show:n」と同値になる \cs_generate_variant:Nn \tl_show:n { x } %→ \tl_show:x が「\exp_args:Nx \tl_show:n」と同値になる
のようなこともできる。