マクロツイーター

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

TeX芸人実力判定問題、その6 (解答編-3)

【躊躇いなく宣伝】
2012/12/01 〜 2012/12/25

TeX & LaTeX Advent Calendar

→18 日目は例のアレ

こちらは平常運行(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
}