2012/12/01 〜 2012/12/25TeX & LaTeX Advent Calendar
こちらは平常運行(ry
* * *
(前回の続き)
\tcln@knumeral の実装の再考
\tcln@kanji
と \tcln@zeropad
が実装できたら、あとは単純に組み合わせれば \tcln@knumeral
は完成しそうである。ところが、実際にはそのようなコードでは正しく動かない。何故だか解るだろうか?
% これはダメ \def\tcln@knumeral#1{% \tcln@kanji{\tcln@zeropad{#1}}% }
規約により、\tcln@kanji
の引数は 12 桁の数字列でなければならない。しかし、上のコードの引数は明らかにそれと食い違っている。確かに引数 \tcln@zeropad{#1}
は「完全展開すれば」数字列になるが、単純に「関数の合成」をしただけでは引数が展開される訳でないので上手くいかないのである。
引数を先に展開しなければいけない、というと、そう、
\expandafter
の出番である。しかし厄介なことに、今のケースだと、\tcln@zeropad{#1}
を展開して数字列を得るためには 4 回展開を施す必要がある……となると、こういう有様になってしまう。((代入が禁止されているので「\edef
で一気に展開させてしまう」という手段が使えないことに注意。))
\def\tcln@knumeral#1{% \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter\tcln@kanji \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter{\tcln@zeropad{#1}}% }
……いや、もちろん、
三度のビールより \expandafter が好き
という人にとってはこれは大いに歓迎すべきことなのかも知れない。それは否定すべくもない。しかし、当然ながら世の中にはそうでない人もいることもまた確かである。((引用のツイート主からの弁明:「もちろん \expandafter
をどうしても 5 個以上並べざるを得ない理由――例えばそれがネタである等――がある場合は別であるということは明確にしておきたい」))
そういう人にとってはこれは惨状という他なく喫緊の対策が不可欠となる。
一番安直な方法は、\tcln@zeropad
を分離するのを諦めてそのコードを \tcln@knumeral
の一部としてしまうことである。つまり、\tcln@zeropad
の最後の展開を
\def\tcln@zeropad@c#1/#2\relax{% \tcln@kanji{#2#1}% ←ここに \tcln@kanji の適用を組み込む }
という形に変えて、その修正を行った \tcln@zeropad
を \tcln@knumeral
という名前に変えればよい。
これで一応正しい \tcln@knumeral
を実装することはできる。しかしそのためにコードのモジュール化が犠牲になったことは確かである。何とかして、\tcln@zeropad
を分離したまま \tcln@kanji
と合成することはできないか。それを実現するためのやや特殊な技巧を紹介する。それは、「後で他の関数と合成する予定のある関数」について、「後で適用する関数」を指定する引数を最後に追加するのである。つまり、今の場合は \tcln@zeropad
の仕様を次のように変更する。
% \tcln@zeropad{<n>}\CS % (複数回で) \CS{nのゼロ付12桁表記} に展開される. \def\tcln@zeropad#1#2{...}
仕様変更後の実装としては、最後の結果のトークン列について、単に受け取った制御綴 \CS
を適用する形に変えればよいだけである。しかも、上のように \CS
を最後の引数にしておくと、展開が複数の段階にまたがる場合に、最後の段階のマクロ以外は何も変更する必要がなくなる。つまり、\tcln@zeropad
、\tcln@zeropad@a
、\tcln@zeropad@b
はそのままでよくて、\tcln@zeropad@c
だけを次のように変えれば済む。((これは、\CS
が最後(\tcln@zeropad@c
)の段階まで「直後の位置」に残り続けるからである。よく解らない人は実際に展開ステップを書き出してみよう。))
\def\tcln@zeropad@c#1/#2\relax#3{% 最後に \CS を受ける引数を追加 #3{#2#1}% ←\CS の適用を組み込む }
これで \tcln@zeropad
は「任意の関数と合成できる」ことになるので部品としての独立性が保たれることになる。((単独で使いたい場合は \CS
を恒等関数(LaTeX の \@firstofone
)とすればよい。))そして、合成関数 \tcln@knumeral
の定義は以下のようになる。
\def\tcln@knumeral#1{% \tcln@zeropad{#1}\tcln@kanji }