マクロツイーター

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

去年のアレ(非常に)を振り返ってみる

ステマ

☃︎
 
TeX & LaTeX Advent Calendar ☃︎
 

2018/12/01 〜 2018/12/25
〜とにかくLua(La)TeXしよう〜

TeX & LaTeX アドベントカレンダー 2018

*  *  *

昨年(2017 年)の「TeX & LaTeX Advent Calendar」はこんな感じでした。

12/01zr_tex8rTeXでつくるプロポーショナル組日本語文書
12/02wtsnjpPackage Gotoh: TeX で生物配列アライメント
12/03golden_luckyTeXでつくる『RubyでつくるRuby』
12/04abenori行取り
12/05imaginarywisdomお手軽「自分専用パッケージ」のすすめ
12/06trueroadLilyPondとGhostscriptとTeXで作る楽譜と文章を組み合わせたドキュメント
12/07yuracoLaTeX Pub.
12/08munepi[改訂第7版]LaTeX2e美文書作成入門 ヒラギノフォントパッチ
12/09hak7a3PDFの注釈機能で遊ぶ
12/10aminophenTeX Live 2017 注目ポイントまとめ
12/11isaribi_saitohTeXでつくる可能な限りWYSIWYGな文書
12/12tattsanTeXで不動産投資
12/13mattskalatikzpeopleで新しいキャラ
12/14p_typoLuaLaTeXで貂明朝
12/15_MadChemiker_LaTeXでつくる注釈・補足情報の入った図 (の入った文書)
12/16keisuke495500JupyTeX(LaTeX版Jupyter)を作った
12/17uwabamiDebian 9.x (Stretch) での TeX 環境
12/18munepiインストーラープロファイルを用いてTeX Liveをインストールしよう
12/19doraTeX“TeXでつくる”小ネタ集
12/20domperorExcelの表をそのままTeXにコピペで貼り付ける話
12/21bd_gfngfnLaTeXで括弧を自動で補うマクロを書く
12/22VoDMETAFONT で踊る
12/23wtsnjpTeX で作る時計
12/24golden_luckyTeXでつくるMarkdownパーサ
12/25zr_tex8rTeXで覗くシステムプログラミングの世界(えっ)
\Finalezr_tex8r今年も Merry TeXmas! ― \end{texadvent2017}

というわけで、今年はこれをカラッキシ参考にせずに、自分が非常に素敵だと思うネタを全力でぶつけていきましょう!

今年も非常に TeX で Advent Calendar する件について

やります!!

TeXLaTeX Advent Calendar 2018

とっておきの TeXLaTeX ネタを皆で持ち寄って楽しむ
TeXLaTeX Advent Calendar」
今年で 7 回目の開催となります。
皆さんの、心をこめた非常に素敵なネタをお待ちしております!
ハッシュタグは「#texadvent2018
TeXLaTeX 初心者大歓迎。 (重要)
TeXLaTeX 非初心者大歓迎。

今年の重点テーマは……コレです!

今年の重点テーマ

今年の重点テーマはコレです。
「とにかく Lua(La)TeX しよう」

v1.0 のリリースから 2 年を迎え、新しいエンジンである LuaTeX に関心を持つ人も増えていますが、
残念ながら、まだまだ情報(特に日本語文書作成に関するもの)が不足しているように思います。 LuaTeX/LuaLaTeX に関する知見を広めていきましょう。

LuaTeX がもっと普及すべきだと思っている皆さん、その理想の実現に必要なのはあなたの書く貴重な記事です。TeX 界隈の人々に「LuaTeX を使ってみよう」と思わせるような、そんな素敵な記事を書いてみましょう。

例によって「重点テーマ」は「制限」ではありません。

(前略)……以下の何れかテーマに該当する何かを書きます。

  • 「とにかく Lua(La)TeX しよう」に非常に関連する TeXLaTeX ネタ。
  • 「とにかく Lua(La)TeX しよう」にチョット関連する TeXLaTeX ネタ。
  • 「とにかく Lua(La)TeX しよう」にサッパリ関連しない TeXLaTeX ネタ。

このように、以前と同じく、TeX に関連するもの(LaTeX、plain TeX、ConTeXt、TeX Live、METAFONT、MetaPost、Asymptote、pandoc、SphinxRe:VIEW)なら何でも構いません。

皆さんの、心温まる TeX ネタで寒い冬を乗り越えましょう☃︎

pxchfon の unicode オプションについてもうチョット語ってみる

(u)pLaTeX + dvipdfmx のワークフローにおいて、何らかの和文文字について、

その文字は Unicode に入っているし、
今使っているフォントにも入っているはず

なのに文字が“なぜか出力できない”、という奇妙な不具合に出会ったら、pxchfon の unicode オプションを試してみよう。

もっと丸数字したい話
% pLaTeX 文書
\documentclass[a4paper]{jsarticle}
\usepackage{otf}
\begin{document}
\ajMaru{10}\ajMaru{20}\ajMaru{30}\ajMaru{40}\ajMaru{50}%
\ajSnowman
\ajKuroMaru{10}\ajKuroMaru{20}
\end{document}
% upLaTeX 文書; UTF-8
\documentclass[uplatex,a4paper]{jsarticle}
\begin{document}
⑩⑳㉚㊵㊿☃❿⓴
\end{document}

この例では、㊵(丸40)や㊿(丸50)といった丸数字の一部および黒丸数字が出力されていない。しかし、upLaTeX 文書ソースに直接書けていることから判るように、これらの文字は Unicode に含まれている。*1ここで実際に使われているフォントは「IPAex 明朝」である(☃の字形を見れば判る)が、このフォントは先に挙げた丸数字に対応している。(これはワープロソフト等で「IPAex 明朝」を指定して当該の文字を書いてみれば判る。)

つまり、この不具合は“なぜか出力できない”の例である可能性がある。

そういうわけで、早速 unicode 付きの pxchfon を試してみることにしよう。従来通り IPAex フォントが使われてほしいので、プリセットとして ipaex を指定する。

% pLaTeX 文書
\documentclass[a4paper]{jsarticle}
\usepackage{otf}
\usepackage[ipaex,unicode]{pxchfon}
\begin{document}
\ajMaru{10}\ajMaru{20}\ajMaru{30}\ajMaru{40}\ajMaru{50}%
\ajSnowman
\ajKuroMaru{10}\ajKuroMaru{20}
\end{document}
% upLaTeX 文書; UTF-8
\documentclass[uplatex,a4paper]{jsarticle}
\usepackage[ipaex,unicode]{pxchfon}
\begin{document}
⑩⑳㉚㊵㊿☃❿⓴
\end{document}

スバラシイ。

縦組でダブルミニュートしたい話

upLaTeX + dvipdfmx の環境で、実際に使われているフォントが IPAex フォントである場合、縦組においてダブルミニュート(〝 〟)を使うと、閉じ側の文字が正常に出力されない。

% upLaTeX 文書; UTF-8
\documentclass[uplatex,tate,book,paper=a4]{jlreq}
\begin{document}
ここから判るように、静的型付けには
明らかな優位性が〝存在〟する。
(実際に存在するとは言ってない。)
\end{document}

先の例と同じく、IPAex フォント自体は縦組のダブルミニュートを正しく扱える。(ワープロソフト等で確認できる。)従って、これも“なぜか出力できない”現象だと考えられる。

というわけで、再び pxchfon + unicode を試してみると……。

% upLaTeX 文書; UTF-8
\documentclass[uplatex,tate,book,paper=a4]{jlreq}
\usepackage[ipaex,unicode]{pxchfon}
\begin{document}
ここから判るように、静的型付けには
明らかな優位性が〝存在〟する。
(実際に存在するとは言ってない。)
\end{document}

スバラシイ。

*1:Unicode には丸数字の 1〜50 と黒丸数字の 1〜20 が含まれている。

unicode-math を完全に理解したい話(3)

前回の続き)

unicode-math の理論を完全に理解したい(続き)

入力における立体と斜体の自動補正

「立体と斜体の“非区別”」の小節で述べた通り、数式中のラテン小文字は普通は斜体が使われる。従って、従来の LaTeX 数式では x を入力すると「斜体の x」が出力される。しかし、これまでに述べた理屈をそのまま適用すると、unicode-math では x(U+007B) の入力では「立体の x」が出力されて、「斜体の x」を出力するには U+1D465 の文字または \symit{x} を入力する必要が生じてしまう。

この面倒を避けるための対処として、unicode-math では「入力の立体と斜体を自動的に補正する」「立体と斜体を区別しない特殊な数式英字フォント命令を用意する」という入力補正を行っている。

  • up の(“基本”の)文字、it の文字の入力 → \symup または \symit に補正される
  • bfup の文字、bfit の文字、\symbf 命令の入力 → \symbfup または \symbfit に補正される
  • sfup の文字、sfit の文字、\symsf 命令の入力 → \symsfup または \symsfit に補正される
  • bfsfup の文字、bfsfit の文字、\symbfsf 命令の入力 → \symbfsfup または \symbfsfit に補正される

※具体的な数式英字フォント命令(\symup\symsfit 等)は入力補正の対象にならない。

先述の通り「立体と斜体の何れを常用するか」に様々な習慣があるため、入力補正の方式をオプション*1で選択できる。up 対 it の補正結果は math-style オプションの値により決まる。

オプション値 ラテン小 ラテン大 ギリシャ ギリシャ
ISO it it it it
TeX it it it up
french it up up up
upright up up up up

literal指定で補正が抑止され、入力の立体と斜体の区別が保持される。

bfup 対 bfit の補正結果は bold-style オプションの値により決まる。((先述の通り、math-style の TeX の設定は米国の習慣を反映したものといえそうだが、bold-style の TeX、特に「ギリシャ小文字を斜体とする」については注意が必要だと思う。単なる「LaTeX 者の習慣」なのかもしれない。))

オプション値 ラテン小 ラテン大 ギリシャ ギリシャ
ISO bfit bfit bfit bfit
TeX bfup bfup bfit bfup
upright bfup bfup bfup bfup

literal指定で入力補正が抑止され、入力の立体と斜体の区別が保持される。

sfup 対 sfit、および bfsfup 対 bfsfit の補正結果は sans-style オプションの値により決まる。

オプション値 意味
italic sfit および bfsfit
upright sfup および bfsfup

literal指定で入力補正が抑止され、入力の立体と斜体の区別が保持される。

※ところで、Unicode の「数式書体付文字」においては、ギリシャ文字サンセリフ太字(bfsf)をもつが単なる太字(sf)はもたないという規定になっている。何故なんだろう?

※bold-style と sans-style の既定値は math-style の設定値に依存する。math-style の既定値は TeX である。

math- 設定値 bold- 既定値 sans- 既定値
ISO ISO italic
TeX TeX upright
french upright upright
upright upright upright
literal literal literal
ナブラ記号と偏微分記号

ナブラ記号(∇;U+2207)と偏微分記号(∂;U+2202)は Unicode で「数式書体付文字」として扱われている。*2従って、これらの文字も「数式英字」の範囲に含まれ、また入力補正の対象となる。入力補正の方式は各文字ごとに個別のオプションで指定する。

ナブラ記号の補正結果は nabla オプションの値により決まる。偏微分記号の補正結果は partial オプションの値により決まる。

  • italic: 斜体(it/bfit/bfsfit)になる。
  • upright: 立体(up/bfup/bfsfup)になる。
  • literal: 入力補正が抑止される。

nabla の既定値は upright、partial の既定値は italic であるが、math-style が literal の場合は両方の既定値が literal に変わる。

\symliteral 命令

オプションの設定に関わらず、\symliteral 命令を利用すると入力の補正を抑止できる。例えば、math-style が literal 以外の場合、x(U+007B) と U+1D465 は同じ出力(立体か斜体の何れか一方)になるが、\symliteral の引数に入れた場合は x は立体、U+1D465 は斜体で出力される。

*1:“オプション”はパッケージオプションや物理フォント設定関連の命令のオプションとして指定できる。

*2:この 2 つの文字のサポートする数式書体の範囲はギリシャ文字と同じである。つまり単なるサンセリフは無い!

unicode-math を完全に理解したい話(2)

前回の続き)

unicode-math の理論を完全に理解したい

「数式英字(sym系)」と「テキスト数式英字(math系)」の分離

unicode-math では、「単文字と複数文字の区別」を斜体以外の数式書体にも適用する。単文字識別子用の文字を「数式英字(math alphabet)」と呼び、複数文字識別子用の文字を「テキスト数式英字(text math alphabet)」と呼ぶ。*1後者にはテキストの欧文文字と同じ組版規則(リガチャなど)が適用される。

  • 「数式英字」の数式書体の区別については、Unicode の「数式書体付文字」で表す
    • 例えば、「斜体の x」(従来 LaTeX$x$ )は、論理的には“U+1D465 MATHEMATICAL ITALIC SMALL X”で表す。(※ただし後述の入力規則があるため、結局は従来通り $x$ で入力できる。
    • ただ「数式書体付文字」の入力は難しいので、命令による代替表記が用意されて、U+1D465 の代わりに \symit{x} と書くことができる。(MathML の fontvariant 属性のようなもの。)数式英字の書体指定用の命令は \sym<ラベル> という名前をもつ。*2引数には“基本の文字”*3を書く。
    • \sym<ラベル> による数式書体の変更は「別の文字に変えている」のであって、「別のフォントに変えている」のではないことに注意。
  • 「テキスト数式英字」への切替は、\math〜 という命令によって行う。すなわち、従来の LaTeX における数式英字フォント命令 \math〜 は単文字と複数文字の両方に使われたのに対し、unicode-math では \math〜複数文字用に限定したわけである。
    • テキスト数式英字命令は実際にフォントを切り替える。\math〜 の引数に指定するのは“基本の文字”であり、その文字がそのまま出力される。例えば、\mathit{x} は「斜体のフォント」の U+0078(基本の‘x’、U+1D465 ではない)の字形(これは「斜体の x」のはずである)が出力される。
    • フォントの切替であるため、Unicode「数式書体付文字」とは無関係である。\math〜 の部分も「数式書体付文字」のラベルではない。
    • 既定では、次の 5 つのテキスト数式英字命令が提供される。そして、これらはテキストフォントの設定と連動するようになっている。
      命令 説明 次のテキストフォントと同じ
      \mathrm 立体 \rmfamily\mdseries\upshape
      \mathbf 太字立体 \rmfamily\bfseries\upshape
      \mathit 斜体 \rmfamily\mdseries\itshape
      \mathsf サンセリフ立体 \sffamily\mdseries\upshape
      \mathtt 等幅 \ttfamily\mdseries\upshape
      \mathrm には \mathup という別名が存在する。((数式英字命令との整合性をとるなら \mathup が好ましく、従来との互換性をとるなら \mathrm が好ましいので、非常に悩ましい。))
    • テキスト数式英字命令はユーザが自由に追加定義できて、その際には使用するフォントを指定する。そのためのユーザレベル命令 \setmathfontface も用意されている。
\symnormal 命令

\symnormal 命令は、外側の数式英字命令(\sym〜)やテキスト数式英字命令(\math〜)の効果を打ち消して、英字書体の設定を初期状態に戻す。

\mathnormal\symnormal の別名である。

数式フォントの“軸”を再び考える

数式フォントは次の“軸”をもつのであった。

  1. 数式英字フォントの選択
  2. 数式バージョン
  3. フォントサイズ(テキストフォントと共通)

このうち、2 の数式バージョンについては、unicode-math でも扱いは全く変わらない。*43 は明らかに変わらない。1 については次のように変わる。

  • テキスト数式英字フォントの選択
    • 無指定:この場合は「数式英字」のフォントが使われる。従来の「数式イタリック」をはじめとして多彩な「数式書体付文字」が、この一つのフォントに含まれる。「数式書体付文字」は数式英字命令(\sym〜)で代用できる。
    • \mathrm:立体
    • \mathbf:太字立体
    • \mathsfサンセリフ立体
    • \mathit:斜体
    • \mathtt:等幅
    • ユーザ(パッケージ)により追加可能。\setmathfontface 命令が用意されている。

結局、根本的な枠組はあまり変わっていないようで、従来の「数式英字フォント」という概念が用途の限定された「テキスト数式英字フォント」に置き換わっただけ、ともいえそう。

※前述の通り、\mathcal は実用上は単文字識別子用であると見なせる。つまり「テキスト数式英字」ではなく「数式英字」の方に本来は属するため、代わりに \symscr を使うべきであろう。一応互換性のため \mathcal 命令が \symscr の別名として定義されている。((厳密にいうと \mathcal\symcal の別名であり、その \symcal は「ラテン大文字専用の \symscr」に相当するもので、\symscr 自体で常に代替できるため、あまり存在意義はないと思う。(恐らく“cal”という文字範囲を定義したためその副産物として生じたのであろう。)))さらに \mathfrak\symfrak の別名として定義されている。((他にも、\sym〜 のうち「本物の \math〜 命令と重複するもの(up、it、tt)」以外の全てについて、\math〜(例えば \mathbb\mathbfit 等)が別名として定義されている。))しかし、これらの互換用命令は「\math〜 という名前なのに実体が数式英字命令である」という点で非常に紛らわしいので、使用は避けるべきであろう。

unicode-math では \mathnormal(= \symnormal)は「無指定」状態に戻す働きをもつ。

*1:unicode-math のマニュアルでは「テキスト数式英字」について「legacy math alphabet」という用語も用いられているが、両者の数式英字は使い分けるべきものであって、テキスト数式英字が“obsolete”となったわけでは決してないので、この用語は紛らわしいと思う。

*2:「ラベル」は「Unicode の『数式用の書体指定付き文字』」の節で紹介した、it、frak、bfup などの文字列のこと。

*3:テキストと共用されている、本来は立体用の文字のこと。

*4:数式バージョンが変わると、フォントが変わることになる。

unicode-math を完全に理解したい話(1)

unicode-math パッケージにおける数式フォントの扱いについて学習中なので、学習メモをてきとーに書き流しておく。

※数式フォントの中でも、特に数式英字フォント(数字、ラテン文字ギリシャ文字の類)に焦点を当てている。

前提知識

従来の LaTeX における数式フォントの扱い

従来の LaTeX にいて、数式フォントは次の“軸”をもつ。

  1. 数式英字(math alphabet)フォントの選択
    • 無指定:この場合、字種ごとに異なる数式英字フォントが適用される。((数字は \mathrm、英字は \mathnormal……のようになっている。))
    • \mathnormal:“数式イタリック”((これは「無指定」とは異なることに注意。「無指定」状態に戻す(TeX でいうと \fam=-1 をする)命令は LaTeX にはない。))
    • \mathrm:立体
    • \mathbf:太字立体
    • \mathsfサンセリフ立体
    • \mathit:斜体(イタリック*1
    • \mathtt:等幅立体
    • \mathcal筆記体
    • ユーザ(パッケージ)により追加可能。AMSFonts の \mathfrak(ドイツ文字)や \mathbb(黒板太字)等が有名。
  2. 数式バージョン(math version)。例えば \boldmath とか。((数式バージョンを指定する汎用の命令は \mathversion であり、\boldmath\mathversion{bold} と等価。))
  3. フォントサイズ。これはテキストフォントと共通である。

この中で、一つの数式の中で変更できるのは 1 だけで、2 と 3 は数式開始時点の設定値が全体に適用される。((つまり、数式の中で \boldmath\large を実行しても効かない。))

テキストフォントのサイズ以外の属性、つまりエンコーディング・ファミリ・シリーズ・シェープは数式フォントには適用されない。特に、一つの数式の中の数式フォントは書体に関して単一の軸しか持たないことに注意。((つまりテキストの \textbf{\textit{a}} は両方の設定が効いて「太字の斜体」となるが、数式の \mathbf{\mathit{a}} は単に(無駄に)2 回設定を変えているだけで、単なる斜体になる。))

数式英字フォントが無指定の場合のラテン文字\mathnormal の文字)は斜体であるが、特に単文字の識別子(xE の類)を書くのに適したデザインになっていて、「数式イタリック(math italic*2)」と呼ばれる。これに対して \mathitラテン文字は複数文字の識別子(\mathit{dist} の類)を書くのに用いられる。ところが、このような「単文字と複数文字の区別」は斜体以外の書体には存在しない。\mathrm\mathsf は単文字と複数文字の何れにも用いられる。\mathcal\mathfrak は専ら単文字に用いられる。

Unicode の「数式用の書体指定付き文字」

数式を“言語”の一種とみた場合、「書体が違うだけの文字が別のものを指す」という特異な性質をもつ。これに対応するため、Unicode では“数式用の書体指定付き文字”(以下では「数式書体付文字」と呼ぶ)が登録されている。Unicode で定められている数式用書体の一覧を以下に示す。ここで「ラベル」欄は unicode-math がその書体を指す時に用いているラベルを表す。

ラベル 説明 ラテン小文字 e に相当する文字
up 立体 U+0065 LATIN SMALL LETTER E
it 斜体 U+1D452 MATHEMATICAL ITALIC SMALL E
bb 黒板太字(立体) U+1D556 MATHEMATICAL DOUBLE-STRUCK SMALL E
bbit 黒板太字斜体 U+2147 DOUBLE-STRUCK ITALIC SMALL E
scr 筆記体 U+212F SCRIPT SMALL E
frak ドイツ文字 U+1D522 MATHEMATICAL FRAKTUR SMALL E
tt 等幅 U+1D68E MATHEMATICAL MONOSPACE SMALL E
sfup サンセリフ立体 U+1D5BE MATHEMATICAL SANS-SERIF SMALL E
sfit サンセリフ斜体 U+1D626 MATHEMATICAL SANS-SERIF ITALIC SMALL E
bfup 太字立体 U+1D41E MATHEMATICAL BOLD SMALL E
bfit 太字斜体 U+1D486 MATHEMATICAL BOLD ITALIC SMALL E
bfscr 太字筆記体 U+1D4EE MATHEMATICAL BOLD SCRIPT SMALL E
bffrak 太字ドイツ文字 U+1D58A MATHEMATICAL BOLD FRAKTUR SMALL E
bfsfup 太字サンセリフ立体 U+1D5F2 MATHEMATICAL SANS-SERIF BOLD SMALL E
bfsfit 太字サンセリフ斜体 U+1D65A MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E
  • 「数式書体付文字」をもつのは数字、基本ラテン文字、基本ギリシャ文字((\varphi(U+03D5) 等の数式用異体字、および便宜的にナブラ記号(\nabla;U+2207)と偏微分記号(\partial;U+2202)を含む。))。
    • ただしこれらの文字の全てについて数式用書体の全てが揃っているわけでない。例えば、数字の斜体やギリシャ文字の黒板太字は存在しないし、また黒板太字斜体はごく一部の文字(D、d、e、i、j)にしか存在しない。
  • 「立体」(up) は通常の(数式用でない)文字と共用する。上の例の U+0065 が該当する。
  • 「数式書体付文字」の多くは「Mathematical Alphanumeric Symbols(U+1D400〜1D7FF)」のブロックに収められているが、このブロックが設けられる前に Unicode に存在した文字を「数式用の書体指定付き文字」と統合した事例もある。上の例の U+2147、U+212F が該当する。
立体と斜体の“非区別”

単文字識別子についての、「書体が違うものは別の文字」という原則は、一部の分野(物理とか?)では厳密に守られている。*3しかし数学では、「立体と斜体の区別は利用せずに、字種により片方のみを使用する」という習慣がある。*4

plain TeX では(米国の習慣に従って?)次のような設定になっている。

この辺りの習慣は地域により異なる。昔のフランスでは「ラテン小文字は斜体でそれ以外は立体」という規則が用いられていた。

*1:数式には oblique 体は出てこないので、ここでは italic を「斜体」と呼ぶ。

*2:LaTeX の用語ではないが、plain TeX の用語であるようだ。

*3:例えば、一つの文脈に「立体の m」「斜体の m」「太字斜体の m」が現れて、それらが全て別のものを指すという用法も決して珍しくない。

*4:恐らくは「数式中の識別子は斜体で表す」という習慣が、本文の欧文と紛れる(例えば「There exists a value a and …」)ことを避けるためであったから、ラテン小文字以外では積極的に斜体にする理由がないのだと思う。

*5:LaTeX では「数式バージョンを切り替えることで太字にする」ことで太字が使える。ギリシャ小文字の立体はサポートされないので、結果的に、太字斜体が使われることになる。

TeX でデータ構造する場合のヤヤコシイ話(続き)

前回の続き)

これについてもう少しホンキで考えてみる。

背景

ここでの話題は、TeX 処理系の内部処理における、「制御綴名からそれに対応する制御綴のオブジェクト*1への写像」の実装である。例えば、“foo”という文字列から“\foo に関する情報をもつオブジェクト”を得る、という処理を指す。

このような「文字列から(何かへ)の写像」はハッシュテーブルで実装されるのが一般的である。そして TeX 処理系(Knuth のオリジナルの実装、およびそれを拡張したもの―ー現用の処理系は全てこれに該当する)でもハッシュテーブルが使われている。その実装の効率がどうであるかを問題にしたい。

※以降では漸近的な時間計算量のみを問題にする。

データ構造と制御綴の関係をチョット述べておく

TeX 言語には配列(array)の機能がない。なので、アルゴリズムの実装において配列データ型が必要になった場合は何らかの工夫が必要になる。その一つが「制御綴を用いた擬似配列」である。(もう一つは、前回紹介した“\doリスト”のような「構造をもったトークン列を利用する方法」である。)

「擬似配列」方式においては

myArray := [ Snowman, Duck, Sushi ]

という配列(変数*2)を次のように表す。((\@namedefLaTeX の内部マクロであり、与えられた文字列を名前とする制御綴に対する \def を行う。例えば、\@namedef{foo}{...}\def\foo{...} と等価になる。))

\@namedef{myArray/1}{Snowman}
\@namedef{myArray/2}{Duck}
\@namedef{myArray/3}{Sushi}
%→ \@N を整数変数とすると、"myArray"の \@N 番目の要素は
%   \@nameuse{myArray/\number\@N} で取得できる

ただし、こういう「擬似配列」を広範に用いると、「フツーに組版をするプログラム(というか文書)」と比べて桁違いに多くの制御綴を扱う必要性が生じる。これに対応するため、現在通用している TeX 処理系では、使用可能な制御綴の個数の上限が大幅に引き上げられている(Knuth オリジナルでは 2100、現用では 50 万弱)。

TeX 処理系のハッシュテーブルの実際

基本的に、現用の処理系のハッシュテーブルの実装はチェイン法である。*3パラメタは次のようになっている。

  • Knuth オリジナル: テーブルサイズ=1777、最大容量=2100
  • TeX Live 2018 の実装: テーブルサイズ=8501、最大容量=615000*4

これをみると、現用の処理系のテーブルサイズの値は最大容量と比べて明らかに過小であることが判る。(恐らく“無理やり拡張している”せいでテーブルサイズが増やせないのだと思う。)従って、制御綴の個数が数万のオーダに達した後では、理論上はハッシュテーブルの検索の所要時間は個数に比例することになる。

となると、「擬似配列」を用いたデータ構造・アルゴリズムの時間計算量を解析する場合に、「現実的な範囲」においてこの性能劣化を考慮すべきかが問題になる。

チョット実験してみる

というわけで、調べてみた。

  • 事前に「英大文字 10 文字のランダムな文字列」50 万個からなるリストを作っておく。
  • 実験用文書“Set-n”(n=1,1000,10000,100000,470000*5
    \documentclass{article}
    \begin{document}
    \let\xyzNAMWONSSEY\relax % 全部で100万行
    \let\xyzEMOHOGKCUD\relax
    \let\xyzIHSUSSIXET\relax
    …………
    \end{document}
    
    ここで \xyz... の制御綴の大文字部分は、先述のリストの先頭 n 個の部分リストにあるものを順番に使い、使い切ったら最初に戻る。従って、\xyz... の制御綴は延べで 100 万個出現するが、そのうち異なるものは n 個である。
  • 比較対照用文書“Base”
    \documentclass{article}
    \begin{document}
    \relax % 全部で100万行
    \relax
    \relax
    …………
    \end{document}
    
  • 「“Base”対“Set-1”」の時間差が「let の処理 100 万回にかかる時間」、「“Set-1”対“Set-n”」が「制御綴が n 個に増えたことによる追加の所要時間」になる。
  • TeX 処理系として TeX Live 2018 最新の latex コマンドを使った。
  • 5 回のウォームアップの後、100 回のコンパイル時間を計測した。
結果
文書所要時間平均(秒)標準偏差(秒)
Base2.3830.017
Set-13.3660.027
Set-10003.3830.027
Set-100003.4050.027
Set-1000003.5430.026
Set-4700005.1540.064
  • Base と Set-1 の差: 0.983 秒
  • Set-1 と Set-1000 の差: 0.017 秒
  • Set-1 と Set-10000 の差: 0.039 秒
  • Set-1 と Set-100000 の差: 0.177 秒
  • Set-1 と Set-470000 の差: 1.788 秒
考察小並感

“対数的な視点”でみると、制御綴 100000 個というのは「上限が目の前に迫っている」状況であり、TeX での処理としては「かなり無理がある」状態である気がする。「無理のない範囲」、すなわち 100000 個以下で考えるならば、TeX 処理系のハッシュテーブルの性能劣化は無視してかまわない、のではないかと。

まとめ

(画像省略)

*1:この「オブジェクト」というのは TeX 処理系の実装プログラムが保持するデータのことを指す仮の用語。TeX 言語ではなくて内部処理の話をしていることに注意。

*2:「擬似配列」方式では、配列は変数に束縛された形でのみ存在できる。つまり、「第一級の値」ではない。

*3:Knuth のオリジナルの実装は少し複雑であるが、それから変更が加えられている。

*4:ハッシュテーブルの容量が 615000 なのに上限が 50 万弱となるのは、別の容量制限の影響のため。

*5:470000 はほぼ上限に近い値。(480000 だと容量超過のエラーになった。LaTeX カーネルが“50 万弱”の一部を既に使っていることに注意。)