あるいは 〜私の TeX プログラム変換環境〜
\tclt@remainder
「余りを求める」ことについては、このブログでも何度か題材に取り上げていて、例えば、「TeX で何か関数のようなものを」のシリーズでも扱われている。今回の作業では、関数を「変数 ires に戻り値を返す」手続きに変換したのであるが、それはちょうど「関数」シリーズ(2)の「戻り値をある特定の『変数』に代入させる」という方式と一致する。そして上手い具合にそこに「正解」の実装 \xx@remainder
が載っているのでそれをそのまま使って済ませよう。ただし変数名 (\xx@)a
の代わりに (\tclt@)k
を使う。((特に深い理由はない。この \tclt@k
は新たな変数である。))戻り値を代入する先はもちろん \tclt@ires
である。
\def\tclt@remainder#1#2{% \tclt@k=#1\relax \divide\tclt@k#2\relax \multiply\tclt@k-#2\relax \advance\tclt@k#1\relax \tclt@ires=\tclt@k }
\tclt@split@digit
もう 1 つの \tclt@split@digit
の方はかなり難しい。まず、ゼロ付 4 桁の表記にするところから既に厄介である。でも「関数」の記事をよく見ると、非常に都合がいいことに、「余り」の他のもう一つの題が「ゼロ付 5 桁」にすることではないか! そこのコードを見てみよう。
% function infivedigits(num) % return ((num.."/0000"):sub(1,6):gsub("^(.*)/(.*)$","%2%1")) % end \def\xx@in@five@digits@a#1\xx@nil{% #1 は10進表記 \xx@in@five@digits@b#1/0000\xx@nil} \def\xx@in@five@digits@b#1#2#3#4#5#6#7\xx@nil{% \xx@in@five@digits@c#1#2#3#4#5#6\xx@nil} \def\xx@in@five@digits@c#1/#2\xx@nil{% #2#1}
5 桁でなく 4 桁の場合、Lua のコードは次のようになる。(「0」が 1 つ減って、「6」→「5」になる。)
((num.."/000"):sub(1,5):gsub("^(.*)/(.*)$","%2%1"))
これに対応する変更を施すとなると、どうやら次のようになりそうだ。(名前空間を tclt
にし、マクロ名を tclt@split@digit〜
に変えた。)
\def\tclt@split@digit@a#1\tclt@nil{% #1 は10進表記 \tclt@split@digit@b#1/000\tclt@nil}% 「0」を1つ消した \def\tclt@split@digit@b#1#2#3#4#5#6\tclt@nil{% 「#7まで」を「#6まで」に \tclt@split@digit@c#1#2#3#4#5\tclt@nil}% 「#6まで」を「#5まで」に \def\tclt@split@digit@c#1/#2\tclt@nil{% #2#1}
実際にこのコードは「ゼロ付 4 桁」への変換を行うものになっている。(\tclt@split@digit@a 42\tclt@nil
とかを直接出力させてみよう。)そうだとすると、この最後にある「#2#1
」が「0042
」などの 4 桁の数字になっているはずである。あとはこれを 1 桁ずつ分けて別の変数(\tclt@dm
等)に返せばよい、ということはこの文字列を 4 引数のマクロに続ければよいだろう。\tclt@split@digit@c
の定義を次のように変えてみる。
\def\tclt@split@digit@c#1/#2\tclt@nil{% \tclt@split@digit@d#2#1} \def\tclt@split@digit@d#1#2#3#4{% ......}
これで、4 桁の数字の各々が #1〜#4
に渡る形になった。これをそれぞれ目的の文字列変数に代入するのは容易である。
\def\tclt@split@digit@d#1#2#3#4{% \def\tclt@dm{#1}% \def\tclt@dc{#2}% \def\tclt@dx{#3}% \def\tclt@di{#4}% }
これで全部できたような気がするが、実は非常に大きな問題が残っている。プログラムの実行を追ってみれば判るが、\tclt@split@digit
の引数に渡されるのは \tclt@j
という変数である。しかし、そもそも前提として、\tclt@split@digit@a
に渡す引数(#1
)の整数は十進表記の数字列でなければならない。それならば
\def\tclt@split@digit#1{% #1は整数変数 \tclt@split@digit@a\the#1\tclt@nil }
ではどうか。残念ながらこれだと引数は「\the\tclt@j
」となってしまう。欲しいのは「これを展開した文字列」である。どうすればよいか。
これは展開制御の問題である。……ということは、そう、
\expandafter
の出番なのが普通だが、ここでは敢えて違う方法を述べる。もしここで、\tclt@split@digit@a
と \tclt@nil
が「A
」「B
」という普通の文字であったtおしよう。そうすると、この記事を読んでいる人なら、「A\the\tclt@j B
」の整数の部分を展開した文字列を文字列変数(\tclt@x
としよう)に代入するコードは書けるだろう。
\edef\tclt@x{A\the\tclt@j B}
これで、例えば \tclt@j
が 42 なら \tclt@x
は「A42B
」となる。ここで A
と B
を元の制御綴に戻すと、「\tclt@split@digit@a 42\tclt@nil
」という所望の形になっている。すなわち、「代入」した後に \tclt@x
を「実行」すればよい。ということは、こうすればよいのではないか。
\edef\tclt@x{\tclt@split@digit@a\the\tclt@j\tclt@nil} \tclt@x
このままでは上手くいかない。何故かというと、\tclt@split@digit@a
はマクロなので展開されてしまうからである。また、\tclt@nil
は未定義であり*1これを展開しようとするとエラーになる。しかし、TeX にはこの場合に「展開させない」という働きをもつプリミティブ \noexpand
が存在する。これを使えば正しく動くものが実装できる。
\edef\tclt@x{\noexpand\tclt@split@digit@a\the\tclt@j\noexpand\tclt@nil}
これで、変数 \tclt@x
の中身は本当に「\tclt@split@digit@a
〈\tclt@j
の値の数字列〉\tclt@nil
」になるのである。
\def\tclt@split@digit#1{% \edef\tclt@x{\noexpand\tclt@split@digit@a\the\tclt@j% \noexpand\tclt@nil}% \tclt@x }
*1:マクロ引数の区切りに使われる制御綴は未定義でもよい。展開する対象にならないからである。