「正しい\mathstyle」を求めて

少し前の記事に\mathstyleについて書いたが、この記事ではその辺の話題をもう少し掘り下げる。

目次

言葉遣いであるが、この記事で「実行時に」と書いたら、「TeXコードの」実行時、つまりmath listを構築する段階のことを指す。通常のTeX処理において数式スタイルが確定する段階はmath listの構築が終わった段階であり、この時点も「TeX処理系の」実行時には違いないが「TeXコードの」実行時ではない。

復習

リンク集:

TeXの数式には4つのスタイル(D, T, S, SS)とその変種 (‘cramped’ variants; D’, T’, S’, SS’) が割り当てられるが、TeXコードの実行時にはそのスタイルを取得できず、スタイルに応じて組版内容を変えるには\mathchoiceという醜い手段に頼る必要がある。

LuaTeXは「現在の数式スタイルの近似」を実行時に(「展開時に」と言った方がいいかもしれないが)取得できる\mathstyleというコマンドがあるが、\over系コマンドが絡むと\mathstyleの値は不正確となる。\overは中置で使われるが、分子の部分を実行中に「この後\overが来る」とはわからないからだ。

この問題に対し、LuaTeXでは\over系コマンドが絡んでも\mathstyleで正しいスタイルを取得できるよう、\Ustackというコマンドが用意されている。\Ustackは分数に前置して

\Ustack { a \over b }

という風に使う。

LaTeXの\fracは残念ながら標準では\Ustackを使わないが、lualatex-mathパッケージを読み込むと\Ustackを使うようになってくれる。(lualatex-mathパッケージを直接読み込んでいなくても、unicode-mathパッケージを使っていればlualatex-mathパッケージは自動で読み込まれるので、気づかずに使っている人もいるかもしれない。)

では、\Ustackを使えば\mathstyleは常に正しいスタイルを返すようになるのか?

\mathstyleが正しい結果を返さない可能性

\Ustackを使わなかった場合

すでに説明したように、\Ustackを使わずに

{〈分子〉 \over 〈分母〉}

と書くと〈分子〉の部分で\mathstyleによって正しいスタイルが取得できない。

また、\over系コマンドは\mathstyleに関しては単にその時点での\mathstyleの値をcramped variantに変えるようで、そうすると〈分母〉の部分でも\mathstyleの値が正しくなくなる。

\Ustackを使った場合

\Ustackを使って

\Ustack {〈分子〉 \over 〈分母〉}

と書くと〈分子〉の部分で正しいスタイルを取得できるようになる。めでたしめでたし。…ではない。

普通はしないと思うが、分子の部分でスタイルを変更した場合のことを考えてみよう。

例えば、

$$\Ustack {a \scriptstyle +b \over 〈分母〉}$$

と書いたとしよう。この時、〈分母〉の部分の数式スタイルは T’ だが、この部分で\mathstyleは正しいスタイルを返さない!

\over系コマンドはその時点での\mathstyleの値をcramped variantに変えるとさっき書いたが、この例での \over の時点での\mathstyleは\scriptstyle (S) なので、〈分母〉の部分での\mathstyleは\crampedscriptstyle (S’) となる。

分子の部分で数式スタイルを変更しても分母の部分で\mathstyleが正しいスタイルを返すようにするためには、分子の部分を必ず { } で囲うようにすれば良い。{ } に囲われた部分の数式スタイルの変更は\mathstyleの意味でも局所的となるからだ。

% Good: 〈分母〉の部分でのスタイルは T'
$$\Ustack { {a \scriptstyle +b} \over 〈分母〉}$$

一方、分子を \begingroup \endgroup で囲っても数式スタイルの変更は局所的とならず、\mathstyleは変更されたままである。

% Bad: 〈分母〉の部分でのスタイルは S'
$$\Ustack { \begingroup a \scriptstyle +b \endgroup \over 〈分母〉}$$

つまり、LaTeXの\fracは

\def\frac#1#2{ { \begingroup #1 \endgroup \over #2 } }

という風に定義されている(この定義は実際のものではない)が、正確な\mathstyleを得るためには単に\Ustackをつけて

\def\frac#1#2{ \Ustack { \begingroup #1 \endgroup \over #2 } }

と再定義するのではダメなのだ。lualatex-mathによる\fracの再定義は素朴に\group_begin:, \group_end: (\begingroup, \endgroupのexpl3での名前)を使っているので、分子の部分でスタイルを変更すると分母の部分で正確な\mathstyleが得られなくなる。

\mathchoice中の\mathstyleとcramped variant

\mathchoice中で\mathstyleを使うとどうなるか?

実際に確かめてみると、第1の選択肢の中では\displaystyle, 第2の選択肢の中では\textstyle, 第3の選択肢の中では\scriptstyle, 第4の選択肢の中では\scriptscriptstyleとなっている。まあこれが自然…だろうか?

この挙動は、数式スタイルがcramped variantであっても\mathchoice中の\mathstyleではそれが剥がされてしまう、ということを意味する。

\[
  \frac{a}{
    % 分母での数式スタイルは \crampedtextstyle (T') のはず
    \mathchoice{}{
      % \mathchoiceの中
      \ifcase\mathstyle D\or D'\or T\or T'\or S\or S'\or SS\or SS'\fi
    }{}{}
    % \mathchoiceの外
    \ifcase\mathstyle D\or D'\or T\or T'\or S\or S'\or SS\or SS'\fi 
  }
\]

上記の例の組版結果は \(\dfrac{a}{TT’}\) となる。

\mathchoice中でスタイルを変える場合

普通はしないと思うが、\mathchoice中でスタイルを変えるとどうなるか?

実際のスタイル変更は\mathchoiceの後の部分にも影響するが、実行時には\mathchoiceの引数は { } で囲われているため、\mathstyleの設定は局所的となる。

つまり、

\[
  \mathchoice{
    1+
    \scriptstyle
    % ここでは \scriptstyle
    1+
  }{}{}{}
  % ここも \scriptstyle だが、\mathstyleの値は\displaystyleに戻っている
  1
\]

となる。

正しい\mathstyleを得るにはどうすればいいか

LuaTeXを参考にしつつ新しくTeXっぽい処理系を作るとして、\mathstyleが必ず正しい値を返すようにするにはどうすれば良いかを考えよう。(というのは、筆者が今まさにminoki/yurumathTeXっぽいやつを作っているからそういうことを考えるのだが)

分数と\Ustack

まず、\over系コマンドの直接的な利用を禁止し、\fracや\genfrac等を使わせるようにする。\fracはプリミティブとして提供するか、あるいは

\def\frac#1#2{ \Ustack { { #1 } \over #2 } }

という風にマクロとして定義する。分子の部分が \begingroup – \endgroup ではなく波かっこ { } で囲われているのがポイントである。

\fracコマンドをマクロとして提供する場合に処理系側として\over系コマンドの直接的な使用を禁止するためにできることとしては、\Ustackなしの\overを拒絶する、\Ustackの分子の { } で覆われていない部分でのスタイル変更を禁止する、等が考えられる。\Ustackの後に分数が来なかったらエラーにするのはもちろんである。

\mathchoiceの内側での\mathstyle

\mathchoiceの周囲のスタイルがcramped variantかどうかを考慮し、\mathchoiceの内側の\mathstyleに反映させるべきだろう。

\mathchoice中でスタイルを変える場合

まず一つ考えられる案としては、実行時に正しい数式スタイルが分かっているのだから4つの選択肢のうち「正しい」ものだけを実行するというもの。つまり、\mathchoiceをマクロのように実装する。

これは「\mathchoiceが4つの選択肢を全て実行する」と仮定して書かれたコードを壊す可能性がある。「4つの選択肢を全て実行する」という仕様のままで正確な\mathstyleを得るなら、「正しい選択肢を実行した後の\mathstyleを\mathchoiceの後でも反映させる」という動作が良いだろう。

つまり

\mathchoice{ A }{ B }{ C }{ D } E

というコードを\mathstyle=\textstyleの状態で実行した時は、 B の実行後の\mathstyleの値を\mathchoiceの実行後、 E の実行時に反映させる。

関連パッケージ

amsmathを読み込むと、\over系コマンドを直接使った際に警告メッセージを発するようになる。このこと(\over系コマンドを忌避する理由、実行時に数式スタイルが確定しないことの弊害)についてtechnote.pdf(手元のマシンでは$ texdoc technote で読める)に書かれているが、\mathchoiceの他にフォント選択絡みにも影響があるらしい。

まあ\over系コマンドを封じたからといってTeXの仕様自体が変わるわけではないのだが…。

\over系コマンドを乗っ取って警告を出すのはいいが、\frac自身が使う\overで警告が出ては良くない、ということで、amsmathでは(警告の出ない)\overプリミティブを\@@overという名前で保存しておいて、\fracではそちらを使うように再定義する。

\frac系コマンド、subscript, superscript等を上書きすることにより、LuaTeX以外でも\mathstyleをエミュレートし、それを用いて\mathchoiceをマクロとして実装するパッケージ。

…というのは時系列的には正しくない説明で、(GitHubの履歴を見ると)2007年ごろにはmathstyleパッケージはすでに存在したのに対し、LuaTeXに\mathstyleが追加されたのは2009年ごろリリースのバージョン0.40である。つまり、mathstyleパッケージが元ネタでLuaTeXがそれを真似たと考えるべきだろう。mathstyleパッケージの\mathstyleは\chardefされたトークンなのに対しLuaTeXのやつは数字に展開されるコマンドであるという違いは気になるが…。

ところで、LuaTeXの\Ustackに相当する(分数に前置する)mathstyleパッケージのコマンドは\fracstyleだが、\mathstyleの値として(0〜7ではなく)0〜3を設定してしまっている気がする。バグか?↓

なお、このパッケージではcramped variantを正しく取得することは主眼にはないようで、superscriptとsubscriptは同様に扱われるし、\radicalや\overlineにはパッチを当てていない。元々の目的が\mathchoiceをマクロとして再定義することのようだから、cramped variantはどうでも良いのだろう。

この記事の上の方でも触れた。LaTeX2e kernelやamsmathで定義されている\fracコマンドは\Ustackを使わないので、それを使うように上書きする。そのほかにもフォント周りの設定をいじるようだ。

すでに書いたように、\fracの分子は\begingroup〜\endgroupで囲まれているので、\fracの分子中でスタイルを変更すると分母での\mathstyleが正しくなくなるという問題がある。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です