2012/12/01 〜 2012/12/25TeX & LaTeX Advent Calendar
こちらは平常運行。
* * *
問題はこちら。簡単にいうと、e-TeX なしの pTeX で、正整数を「えるたそ名」に変換するような、完全展開可能な「関数」を作れという問題である。完全展開可能であるので次のように「関数」のように使えるはずである。
\edef\result{1→\eltasoname{1}; 1000→\eltasonamae{1000}} \show\result %==> 1→一反田えー; 1000→千反田える
ここでは、問題の \eltasoname
の作り方を順を追って説明する。なお、この記事では plain TeX で実装することとし*1、プログラムで使う名前空間は「tcln」とする。(想定する読者のレベルは一般の TeX 言語の記事と同じである。*2)
完全展開可能の鉄則
このブログでは幾度となく述べられているが、完全展開可能なマクロを作るときに第一に守らなければならない鉄則はこれである。
「代入」(assignment)の操作は禁止
ここで「代入」というのはレジスタの代入文や \advance
等)は全て「複合代入」(C 言語の +=
演算子等)に相当するからである。))も含み、要するに TeX の状態を変化させる全ての操作が含まれる。つまり、関数型言語で「副作用」と呼ばれるものに相当する。これが一切禁止されているということは、完全展開可能のプログラミングの世界は Haskell のような純粋な関数型言語に似ているということになるのは間違いない。
ただし、TeX 言語自体があまり「完全展開可能でのプログラミング」のことを考慮して設計されていない*3ので、色々と理不尽な制限が課せられることになる。その最たるものが「算術演算が不可能」ということだろう。この「理不尽な制限」を緩和したものが e-TeX 拡張である。しかしこの問題では e-TeX 拡張の使用が禁止されている。すなわち、この問題は理不尽であり、ひいては問題の出題者が理不尽であるということは疑いない所であろう。
「配列」の作成
さて、完全展開可能でない普通の「えるたそ」のプログラム(tceltaso.sty)においては、余りの数から「えー」「びー」……への文字列への変換を「擬似配列」を用いて実現した。配列変数の参照(\csname tcln@alpha/\the\tcln@k\endcsname
)は完全展開可能だから、この方法は完全展開可能な実装のときにも有効である。さらに大事なことは、配列データを作成するのは、(完全展開可能を保証する必要がある)\eltasoname
の実行(展開)時ではなく、プログラムの読込時となるので、ここでは完全展開可能の制約を気にする必要が全くない。なので、tceltaso.sty で用いていたロジックがそのまま通用することになる。
ただし、今は LaTeX ではなく plain TeX を使っているので、\@for
が使えない。だから次のようなパターンを利用する。
\@for\tcln@x:=ぜっと,えー,びー,...\do{<\tcln@x を用いたコード>} ↓ \def\do#1{<#1 を用いたコード>} \do{ぜっと}\do{えー}\do{びー}...
これは所謂「\do
-リスト」の利用と見做すことができる。((だから \do
という(自身の名前空間にない短い)制御綴が使える。))これに従って alpha
と digit
の配列を作成するコードを書くと以下のようになる。*4
\newcount\tcln@x \def\do#1{% \expandafter\def\csname tcln@\tcln@tempa/\the\tcln@x\endcsname{#1}% \advance\tcln@x 1 } \tcln@x=0 \def\tcln@tempa{alpha} \do{"ぜっと}\do{えー}\do{びー}\do{しー}\do{でー}\do{いー}\do{えふ}% \do{じー}\do{えいち}\do{あい}\do{じぇー}\do{けー}\do{える}\do{えむ}% \do{えぬ}\do{おー}\do{ぴー}\do{きゅー}\do{あーる}\do{えす}\do{てぃー}% \do{ゆー}\do{ぶい}\do{だぶりゅー}\do{えっくす}\do{わい} \tcln@x=0 \def\tcln@tempa{digit} \do{}\do{一}\do{二}\do{三}\do{四}\do{五}\do{六}\do{七}\do{八}\do{九}
\eltasoname の設計試案
\eltasoname
を実装するのに必要な部品(関数)は次の 2 つである。
\tcln@knumeral
: 整数を漢数字に変換する。(例: 42 → 四十二)\tcln@index
: 整数に対応するアルファベットの名前の添字(つまり 26 で割った余り)を求める。(例: 42 → 16)
ここで、\tcln@knumeral
はさらに 2 つの部品(関数)に分けることができる。
\tcln@zeropad
: 整数をゼロ付きの 12 桁の十進表記で著す。(例: 42 → 000000000042)
※整数は最大 10 桁であるが、漢数字への変換を考えると 12 桁で表す方が便利である。\tcln@kanji
: 12 桁の数字を漢数字に変換する。(例: 000000000042 → 四十二)
ここで「関数」の取扱に注意が必要である。「変換して TeX プログラミング」において全面的に採用したように、通常の TeX プログラミングでは「関数」を「特定の変数への代入」の形で実現するのが一つの「容易な方法」であった。これに対して、完全展開可能の場合は代入が使えないので、関数をそのまま関数のような形式で表すことになる。
%%(公開) \eltasoname{<整数n>} % 整数nの「えるたそ名」. \def\eltasoname#1{% \tcln@knumeral{#1}反田% \csname tcln@alpha/\tcln@index{#1}\endcsname } %% \tcln@knumeral{<整数n>} % 整数nの漢数字表記. \def\tcln@knumeral#1{% \tcln@kanji{\tcln@zeropad{#1}}% } %% \tcln@zeropad{<整数n>} : 12桁の数字列に変換. %% \tcln@kanji{<数字列>} : 漢数字表記に変換. %% \tcln@index{<整数n>} : アルファベットの名前の添字.
それでは、3 つの部品(\tcln@zeropad
、\tcln@kanji
、\tcln@index
)の実装を進めていこう。