マクロツイーター

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

それでも TeX でプログラミングしたい人のための何か (14)

あるいは 〜私の 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」となる。ここで AB を元の制御綴に戻すと、「\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:マクロ引数の区切りに使われる制御綴は未定義でもよい。展開する対象にならないからである。