LaTeXのカウンタ値の出力の仕方として,
\newcounter{test} \setcounter{test}{123} \arabic{test} % => 123
というようなのはお馴染みです。 そこで,カウンタ値を半角数字ではなく全角数字で出力する,というお題を考えましょう。
\kansuji
による変換
こういうときは,(u)pTeX のプリミティブである \kansuji
を使うと簡単です。\kansuji
は,続く数字の文字列を漢数字の文字列へと展開する命令です。
\kansuji123 % => 一二三
そこで,\kansujichar
によって漢数字として対応づけられる和文文字を,漢数字ではなく全角数字へと変更します。すると,\kansuji
によってカウンタ値が全角数字として出力されるようになります。
\newcounter{test} \setcounter{test}{123} \begingroup \kansujichar0=`0 \kansujichar1=`1 \kansujichar2=`2 \kansujichar3=`3 \kansujichar4=`4 \kansujichar5=`5 \kansujichar6=`6 \kansujichar7=`7 \kansujichar8=`8 \kansujichar9=`9 \kansuji\arabic{test}% => 123(全角) \endgroup
カウンタ値出力用にコマンド化する
上記のコードをそのままコマンド化して,\zarabic
という新たなカウンタ出力命令を作ってみましょう。latex.ltx
の \arabic
の定義にならって,次のように \zarabic
を定義します。
\def\zarabic#1{\expandafter\@zarabic\csname c@#1\endcsname} \def\@zarabic#1{% \begingroup \kansujichar0=`0 \kansujichar1=`1 \kansujichar2=`2 \kansujichar3=`3 \kansujichar4=`4 \kansujichar5=`5 \kansujichar6=`6 \kansujichar7=`7 \kansujichar8=`8 \kansujichar9=`9 \kansuji\number#1% \endgroup}
これを使ってみると,問題なく機能しているように見えます。
\newcounter{test} \setcounter{test}{123} \zarabic{test} % => 123(全角)
問題点
ところが,このように定義した \zarabic
には,元の \arabic
と異なり,完全展開によって文字トークンの列にまで展開できないという問題点があります。そのため,\csname...\endcsname
のような完全展開性を要求される文脈下で使えません。
\newcounter{test} \setcounter{test}{123} \csname command\arabic{test}\endcsname % \command123 へと展開される \csname command\zarabic{test}\endcsname % \command123 へと展開されて欲しいがエラーとなる
解決策
完全展開可能な \zarabic
を定義するため,\def
のパターンマッチを用いて再帰的に文字列置換を行うという手法で \zarabic
を実装し直してみましょう。
\def\zarabic#1{\expandafter\zarabic@ZERO\the\csname c@#1\endcsname 0\relax} \def\zarabic@ZERO#10#2\relax{% \if"#2"% \zarabic@ONE#11\relax \else \zarabic@ONE#11\relax 0\zarabic@ZERO#2\relax \fi} \def\zarabic@ONE#11#2\relax{% \if"#2"% \zarabic@TWO#12\relax \else \zarabic@TWO#12\relax 1\zarabic@ONE#2\relax \fi} \def\zarabic@TWO#12#2\relax{% \if"#2"% \zarabic@THREE#13\relax \else \zarabic@THREE#13\relax 2\zarabic@TWO#2\relax \fi} \def\zarabic@THREE#13#2\relax{% \if"#2"% \zarabic@FOUR#14\relax \else \zarabic@FOUR#14\relax 3\zarabic@THREE#2\relax \fi} \def\zarabic@FOUR#14#2\relax{% \if"#2"% \zarabic@FIVE#15\relax \else \zarabic@FIVE#15\relax 4\zarabic@FOUR#2\relax \fi} \def\zarabic@FIVE#15#2\relax{% \if"#2"% \zarabic@SIX#16\relax \else \zarabic@SIX#16\relax 5\zarabic@FIVE#2\relax \fi} \def\zarabic@SIX#16#2\relax{% \if"#2"% \zarabic@SEVEN#17\relax \else \zarabic@SEVEN#17\relax 6\zarabic@SIX#2\relax \fi} \def\zarabic@SEVEN#17#2\relax{% \if"#2"% \zarabic@EIGHT#18\relax \else \zarabic@EIGHT#18\relax 7\zarabic@SEVEN#2\relax \fi} \def\zarabic@EIGHT#18#2\relax{% \if"#2"% \zarabic@NINE#19\relax \else \zarabic@NINE#19\relax 8\zarabic@EIGHT#2\relax \fi} \def\zarabic@NINE#19#2\relax{% \if"#2"% #1% \else #19\zarabic@NINE#2\relax \fi}
このように \zarabic
を完全展開可能な形に定義しておけば,
\csname command\zarabic{test}\endcsname
のような用法が通ることになります。
この\def
のパターンマッチを用いて再帰的に文字列置換を行うという手法は,今回の「半角数字→全角数字」に限らず,TeXのトークン列に対して
hoge → あいうえお fuga → かきくけこ piyo → さしすせそ ……
といった,完全展開可能なままいくつもの規則に基づく全置換を一括で行いたい場面で活用できることでしょう。
ランダムテストしてみる
今回定義した \zarabic
が,「同じ数字を複数回含む場合」など,あらゆる場合について正しく機能しているかどうかをテストするために,ランダムな入力を与えて \kansuji
による出力結果と比較しまくるテストを実施してみましょう。
TeXにおける乱数生成の手法はかつて TeX & LaTeX Advent Calendar 2018 の記事にまとめました。
今回は,\kansuji
による出力結果と,今回定義した \zarabic
による出力結果が一致するかどうかを,0~999の値について全数チェックすると同時に,pgf
パッケージが提供する \pgfmathrandom
を使って乱数を1000個生成し,それらの入力値についても確かめてみましょう。
上記レポジトリの test-zarabic.tex
をコンパイルすると,次のような出力が得られ,全テストケースに成功している様子が分かります。