マクロツイーター

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

\theナントカ で算術演算する話(2)

前回の続き)

さて、前回の話では、せいぜい「1 を足す」、あるいは算術演算を行う程度のことだったので、intcalc パッケージを使うくらいで事なきを得た。しかし、これが本質的に完全展開可能にできない要素、例えば「カウンタの値によってフォントを変える」とかを含む話になると、同じ方法は通用しなくなる。

というと、昔からの本ブログの愛読者である貴方であれば、きっとこの実力判定問題のことを思い出したに違いない。あの時は、すぐ後に LaTeX の頑強性の概念について解説を試みたのであるが、どうも上手く説明できている気がしなかった。そういうわけで、今回は「悩ましいカウンタ出力命令」の話に絞った話をしたい。

今回の問題

つまり解決すべき課題はコレである。*1

LaTeX カウンタ hoge があるとき、hoge の出力命令 \thehoge を「hogeの値+1の算用数字の後にドット」という形式になるように定義したい。(すなわち“hoge の数値”(\c@hoge の値)が 42 の場合、“hoge の出力”(\thehoge の出力)は“43.”となるべき。) 「1 を足す」のに(e-TeX 拡張でもなく自分または他人の変態コードでもなく) \advance を普通に使って、かつ相互参照が正常に機能させることができるか?

「悩ましいカウンタ出力命令」が悩ましいのは、もちろん脆弱(fragile)であってはいけないのだが、単に頑強(robust)であるだけでは不十分だからである。

「出力命令」が上手くいく条件

すなわち、具体的には次のような条件を満たす必要がある。

  1. \thehoge を保護無しの(“普通の”)状態((\protect = \typeset@protect である状態。))で実行すると所望の出力になる。
    • 当然。
  2. \thehoge を保護付で完全展開する(\protected@edef)すると、その時点でカウンタの値が参照され、結果のトークン列に現在の hoge の値の情報(数値でも出力でもよい)が含まれる。
    • hoge の出力”は aux ファイルに書き込まれるので、それを正常に行うためにこの条件が必要。
  3. 2 の結果のトークン列を、保護無しで実行すると、やはり所望の出力になる。なおかつ、その際にはカウンタの値は参照されない。
    • \ref を実行する際には、aux から読みこんだ“\thehoge の値”が出力されるので、そのためにこの条件が必要。ここで「\ref がある箇所での」hoge の値が使われるのは明らかに不合理である。

いくつか例をみてみよう。ここで“hoge の数値”が 42 であり、ゆえに所望の“hoge の出力”は“43.”であるとする。

例①

まずは \advance を用いた素朴な実装を考える。

\def\thehoge{\count@\c@hoge \advance\count@1 \number\c@hoge.}
  1. “普通の状態”での“hoge の出力”は“43.”だから OK。
  2. 保護付完全展開すると「\count@ \c@hoge \advance \count@ 1 42.」となる。「42」が出ているので一応 OK。
  3. そのトークン列を実行した場合は“42.”が出力される。この時に「hoge が参照されない」という条件は満たすが、明らかに結果が違うので NG。

例②

単純に \thehoge を保護付にしてみる。

\DeclareRobustCommand*\thehoge{%
  \count@\c@hoge \advance\count@1 \number\c@hoge.}
  1. “普通の状態”での“hoge の出力”は“43.”だから OK。
  2. 保護付完全展開すると、保護付の \thehoge は展開されないので「\thehoge」が結果となる*2hoge の値が出てこないので NG である。
  3. 2 の結果(「\thehoge」)を実行すると、今度は「\count@ \c@hoge …」に展開されてここで \c@hoge への参照が発生するので NG。

例③

(今の問題の条件からは外れるが)\numexpr を用いて完全展開可能にした場合を考える。

\def\thehoge{\number\numexpr\c@hoge+1\relax.}
  1. “普通の状態”での“hoge の出力”は“43.”だから OK。
  2. 保護付完全展開すると、出力そのものの「43.」になるから OK。
  3. それを実行すると当然“43.”が出力され、かつこの時に hoge は参照されないので OK。

つまり「出力の文字列に完全展開されるもの」が作れるなら、それは自明に条件を満たす。そして、それだからこそ、今は“それが作れない”場合のことを敢えて考えているのである。

*1:piyo の存在は本質ではないので、元の問題を簡略化した。

*2:ここでは保護付のものは“そのまま”になると見なす。実際には違うのだが、そうなると思って支障はない。