マクロツイーター

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

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 万弱”の一部を既に使っていることに注意。)

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



背景

  • 「効率のよいデータ構造・アルゴリズム」を TeX 言語(や expl3 言語)で実装する機運が高まっている[1]。
  • 複雑なデータ構造・アルゴリズムを実装する目的は優れた効率を得るためであり、それゆえ、「形だけ真似たが実装に問題があって本来の効率が得られていない」ようでは、そのデータ構造・アルゴリズムを実装する(少なくとも実用上の)意味はない。「ほぼ常に Θ(n2) 時間かかるクイックソート」の例が有名だろう。
  • 従って「効率のよいデータ構造・アルゴリズムTeX 言語で実装する話」をするためには、少なくとも「理論的な計算量を測る」手段があることが前提条件となる。(※以降では、漸近的な時間計算量に話を限る。)
  • TeX 言語処理系の処理の様態は、通常の RAM 機械と比べると、自明に等価であるとはいえない。
  • すなわち、TeX 言語における「計算のステップ数」の定義を決めたい。

組版とは無関係な計算に限定する。(データ構造の話なので。)

2 つのモデル

組版処理を考えない場合は、TeX 処理系の動作は「トークンを実行すること」(展開可能トークンの場合は展開を実行と見なす)の繰り返しと見なすことができる。ここに着目した最も単純なモデルとして次のようなものを考える。

[モデル①]
トークンの種類に関わらず、1 トークンの実行に 1 ステップかかる。

このモデルに従うと、例えば次のようなマクロ \S が定義されていた場合:

\def\S#1\E{}

以下の 2 つのプログラム(もちろん実質何もしない)の実行はどちらも 1 ステップとなる。

\S A\E
\S AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\E

これは不合理な気がする。TeX 処理系は \S を展開する際に、少なくとも全ての A を走査する必要がある。このような「トークンの読書き」にかかる時間を考慮したのが次のモデルである。

[モデル②]
トークンの実行で m 個のトークンが読み込まれて n 個のトークンが書き出される場合、そのトークンの実行は m + n ステップかかる。

一般の TeX 処理系で漸近的な時間計算量を考える場合、①と②のどちらが妥当であろうか。

トークンの読書き」の時間は無視できるかできないか

TeX プログラミングでデータ構造を表す場合に、「そのサイズに比例する長さのトークン列」が使われることはよくある。次に示す「\doリスト」はその一例である。

\do{2603}\do{26C4}\do{26C7}\do{1F363}\do{1F986}

従って、①と②のどちらを採用するかで、データ構造の実装の時間量に関する優劣の判断が異なってくる可能性は大きい。

もちろん、厳密に漸近的時間量をみるのであれば、「トークンの読書き」にかかる時間はゼロではないのだから、構造のサイズが増加していくと、最終的には「実行」よりも「読書き」の方が有意になり、従って②が妥当ということになるだろう。しかし現実的に考えると、TeX 処理系の実装上の空間量制限のもとでは、せいぜい数千のオーダのサイズのデータしか扱えそうにない。だから、「現実的な範囲」においては「読書き」の時間は無視できる、という可能性も捨てきれない。

チョット実験してみる

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

  • 実験用文書“Len-n”(n=1,10,100,1000 を用意した)

    \documentclass{article}
    \def\S#1\E{}
    \def\A{\S AAA……AAA\E}%←'A'をn個並べる
    \begin{document}
    \A\par %100万回繰り返す
    \A\par
    \A\par
    ……
    \end{document}
    
  • 比較対照用文書“Base”

    \documentclass{article}
    \def\S#1\E{}
    \def\A{}
    \begin{document}
    \par %100万回繰り返す
    \par
    \par
    ……
    \end{document}
    
  • つまり、“Len-n”は“Base”と比べるとマクロ展開 200 万回分の処理が余計に必要になる。“Len-1”と“Len-1000”はトークン実行の回数は等しいが、“Len-1000”の方が「トークンの読書き」に時間がかかる。
  • 従って、「“Base”対“Len-1”」の時間差と「“Len-1”対“Len-1000”」の時間差を比べれば、「現実的にトークンの読書きの時間が無視できるか」が判断できるはずである。
  • TeX 処理系として TeX Live 2018 最新の latex コマンドを使った。
  • 5 回のウォームアップの後、100 回のコンパイル時間を計測した。
結果
文書所要時間平均(秒)標準偏差(秒)
Base2.2850.016
Len-12.4900.027
Len-102.6400.019
Len-1004.1110.025
Len-100018.8370.072
  • Base と Len-1 の差: 0.205 秒
  • Len-1 と Len-1000 の差: 16.347 秒

まとめ

参考文献

[1] ツイッタァー、オレ的タイムライン

(続く)

画期的な関数型組版言語を作ってみた(※ただし画期的)

夏! 酷暑! ゆきだるま☃︎

チョット SATySFi を語ってみる

さて、ナントカ☃︎の日恒例の「本質的な問題を画期的なナニカで解決する」ネタですが、今年は関数型組版言語について考えてみました。

SATySFi のここがスゴイ!

関数型組版言語といえばやっぱり、SATySFi ですね。SATySFi がリリースされてから早くも半年が過ぎました。この間も、開発は着々と進められています。

SATySFi の開発目標は「TeXLaTeX の“よりよい代替”となること」です。このため、TeXLaTeX と同じマークアップ言語処理系を採用しつつ、TeXLaTeX の“アレな点”を克服するための言語設計を採用しています。

  • TeX 言語は極めて“動的な”言語であるため、コードに不備があった場合に支離滅裂な動作が起こり、結果として意味不明なエラーが発生しやすい。LaTeX も結局 TeX 言語上のマクロに過ぎないため同様である。
    →SATySFi は強い表現力をもつ静的型付けを採用している。これにより、“明らかに意味を持たない”間違った記述が、コンパイルの早い段階に、的確な型エラーとして報告される。
  • TeX 言語は全ての実行制御を「トークン列上のマクロ展開」という形で行う。この方式はそもそも通常規模のプログラミングには向いておらず、何よりも世間で広く用いられている言語と大きく異なる思考を必要とする。このことが TeX 言語の習得(つまり LaTeX 上の機能開発)を著しく困難なものにしている。
    →SATySFi はマークアップとプログラミングの文法を明確に分離し、後者ではごく普通の外見の言語(ML 風の関数型言語)を採用している。

まとめると、SATySFi は「非アレな組版言語処理系」を目指しているわけです。

SATySFi のここがダメ!

このようにスバラシイ SATySFi ですが、私の個人的な見解としては、決定的な“弱点”を抱えてると考えています。その弱点とは

型システムが複雑すぎて、一般のヒト類にとって理解困難である

ということです。

もちろんこの“弱点”は「一般的にヒト類は複雑な型システムの理解が不得手である[要出展]」という性質に起因するものであるため、SATySFi 自体の問題ではありません。しかし、逆にいうと、原因が我々ヒト類のもつ生来的性質にあるからこそ、その改善の可能性が著しく限られてしまう、というのもまた確かです。

結局、理論上は的確な型エラーが提示されたとしても、ヒト類がそれが理解できないという事態が必然的に多発することになるでしょう。これは SATySFi の目標から考えて重大な問題であることは間違いありません。

型システムがアレになる、本質的理由

なぜ SATySFi の型システムは複雑なのでしょうか。実はこれは SATySFi が組版用途の言語であることに根源的な理由があります。

つまり、我々の表す文書が複雑な構造をもつがゆえに、SATySFi は複雑な型システムを持たざるを得ないわけです。すなわち、ここで述べた問題は、ある意味で「本質的に解決困難」といってよいでしょう。

本質的な問題、画期的な解決

では、このような「本質的に解決困難」はどう解決すればよいでしょうか。本ブログの読者なら、既にお判りのことだと思います。そう、

出力をゆきだるま☃︎に変えればよい

のです。型システムの複雑さの根源は文書構造の複雑さにあるのでした。従って、文書構造の複雑性を排除して、その内容を☃︎に統一することにすれば、型システムの単純化が実現できるのです。

型システムを極力単純にすることには大きな利点があります。それによって、静的型エラーが起こる可能性を排除することも可能となるためです。つまり、字句解析を通ったプログラムは常に型付け可能になるわけです。さらに、型付けが健全であれば、動的な型エラーが起こることもありません。結果的に、ユーザは言語の型システムについての知識を何も持たなくても済むことになります。すなわち、ヒト類に変更を課する必要性から解放されるわけです。

画期的な静的組版言語「scSATySFi」

そういうわけで、早速作ってみました。

Windows(64bit)の場合は、リリースのページにビルド済のバイナリ(scsatysfi.exe)が置いてあるので、それをダウンロードして適当な(実行パスの通った)ディレクトリに配置すればOKです。他の環境の場合は、README にある通り、Go をインストールして「go get」してください。

とにかく scSATySFi を使ってみる

インストールができたら、早速使ってみましょう。せっかくのナントカの日なので、☃︎を出力してみます。scSATySFi の文法は非常に単純で、☃︎を出力するには、単に☃︎(U+2603)を書くだけでOKです。LaTeX や SATySFi にあるような「文書構造を示すための記述」は一切ありません。

[essential.scty]
☃︎

では早速この文書ファイルをコンパイルしましょう。以下のコマンドを実行します。

scsatysfi essential.scty

以下のような端末出力とともに、PDF ファイル essential.pdf が出力されました。

# scsatysfi essential.scty
 ---- ---- ---- ----
  target file: 'essential.pdf'
  dump file: 'essential.scsatysfi-aux' (won't be created)
  parsing 'essential.scty' ...
 ---- ---- ---- ----
  reading 'essential.scty' ...
  type check passed. (essential)
 ---- ---- ---- ----
  evaluating texts ...
  evaluation done.
 ---- ---- ---- ----
  writing pages ...
 ---- ---- ---- ----
  output written on 'essential.pdf'.

essential.pdf の中身を確認してみましょう。

完璧ですね!

チョット注意
  • scSATYsFi は静的型付き言語ですが、完全な型推論機能をもっているため、ユーザが型を明示的に書く必要は全くありません。実際、scSATySFi には型を明示する記法はありません。
  • もしかしたら(キーボードに「☃︎」のキーがないなどの不幸な理由で)文字「☃︎」の入力が難しい場合もあるでしょう。そのため、scSATySFi では「☃︎」を「8」で代用できるようになっています。(なお「⛄︎」(U+26C4)「⛇」(U+26C7)でも「☃︎」と等価になります。)それ以外の文字は scSATySFi の文法では現れないため、「☃︎」「⛄︎」「⛇」「8」および空白類文字(空白・水平タブ・改行)以外の文字が含まれると文法エラーとなります。((例外として、コメント開始文字である「🦆」が現れた場合、その行の残りの文字は無視されます。また「🦆」は「2」で代用できます。))
  • ☃︎がもっともっと入った文書を作ろうとして次のように書いたとします。
    ☃︎ ☃︎ ☃︎ ☃︎ ☃︎
    
    この文書ファイルはコンパイルは通りますが、残念ながら出力される☃︎は常に一つだけになります。複数の☃︎を扱おうとすると、型システムが複雑になってヒト類の手に負えなくなってしまうからです。一つの文書で済ませるなんて横着をせずに、☃︎の文書をたくさん作りましょう。その方が素敵でしょう。
    ※実際には、「☃︎ ☃︎ ☃︎ ☃︎ ☃︎」は scSATySFi の式であり、それを評価した結果の値が☃︎になります。なので、出力は☃︎一つになるわけです。

scSATySFi のチョット理論的な話

既に述べた通り、scSATySFi ではユーザが型システムを理解する必要は全くないのですが、興味のある人のために、scSATySFi の型システムについて簡単に説明しておきます。

構文・型

型が 1 つしかないという、極限まで単純化した型システムになっています。

簡約規則

この簡約規則について、以下の性質が成立します。*1

  • すべての項が強正規化可能である。
  • Church-Rosser 性(合流性)をもつ。
型付け規則
     

この型付け規則について、以下の性質が成立します。

  • 全ての項は型付け可能である。つまり、静的型エラーが発生しない。
  • 任意の項について、型情報が全く与えられない状態から当該の項の型を推論できる。またその推論を効率的に(線形時間で)行うためのアルゴリズムが存在する。

この 2 つの性質があるため、ユーザはコード作成時・コンパイル時の何れにおいても、言語の型システムについて一切気にする必要がありません。

具象構文について

実は、適用演算については、結合則が成立します。*2

(☃︎ ☃︎) ☃︎ = ☃︎ (☃︎ ☃︎)

従って、一般に☃︎が複数ある式においては、式の値は括弧の位置によらず一定になります。例えば、次の 3 つの式はどれも同じ値をもちます。

(​(​(☃︎ ☃︎) ☃︎) ☃︎) ☃︎
(☃︎ ☃︎) (​(☃︎ ☃︎) ☃︎)
☃︎ (​(☃︎ (☃︎ ☃︎)) ☃︎)

このため、scSATySFi の具象構文においては、「演算の優先順位を表す括弧を常に省略する」という規則を採用しています。これにより、ユーザは☃︎以外の記号を一切書かなくて済みます。

☃︎ ☃︎ ☃︎ ☃︎ ☃︎

scSATySFi のチョット素敵な使い方

  • 文法を極力簡単にすることを優先させたため、出力の☃︎をカスタマイズする機能は敢えて省いています。ただし例外として、☃︎のマフラーの色に限っては、コマンド起動時のオプション“--muffler”で指定できます。
    scsatysfi --muffler="green!50!black" essential.scty
    ここで“--muffler”に指定する値は、LaTeX の xcolor パッケージの「色式」に従います。

まとめ

というわけで、「SATySFi の設計はスバラシイけど、実際に自分が使うのは難しそう……」と思っている人は、ぜひとも画期的な scSATySFi を試してみてください。scSATySFi で ☃︎な文書をどんどん作って、酷暑の夏を乗り切りましょう!

*1:煩雑さを避けるため、この記事では証明は一切省略します。

*2:括弧 ( ) は抽象構文における演算子の優先順位を示す補助記号です。

TeX 言語で goto してみる的なナニカ

Gotoh アルゴリズムが既に使用済、ということは、どうやらマジメに goto を考えるしかない*1ようである。

作ってみた

もちろん出力はごくフツーの FizzBuzz になる。

まとめ

こんなことをしている場合じゃない。

*1:素敵な「goto のロゴ」を出力する、という案も考えたが、あまりにもアレである。

そういえば ZXjafont が新しくなった(v0.4)

もう随分昔のこと(2018 年 5 月)だけど。

本記事では、この版の新機能について解説する。

ZXjafont って何

簡単にいうと、PXchfon とか luatexja-preset の機能にあるような「和文フォントのプリセット設定」を XeLaTeX + zxjatype の環境で行うためのもの。*1

% XeLaTeX文書; UTF-8
\documentclass{article}
\usepackage{zxjatype}% 欧文文書で部分的に日本語を使う
\usepackage[ms]{zxjafont}% 和文をMSフォントに
\begin{document} % 欧文は変わらない(Latin Modern)
{\TeX} is ultra-アレ.
\sffamily {\LaTeX} is アレ-escent.
\end{document}

実は、XeLaTeX 上で BXJS クラスの jafont オプションで和文フォントの種類を設定する場合には zxjafont パッケージが使われる。なので、zxjafont を直接利用する人は少ないとしても、間接的に利用した人はそれなりにいるだろう。

% XeLaTeX文書; UTF-8
\documentclass[xelatex,ja=standard,a4paper,
  jafont=ms]{bxjsarticle}% 和文をMSフォントに
\begin{document} % 欧文は変わらない(Latin Modern)
{\TeX}は激アレ。
\sffamily {\LaTeX}は微アレ。
\end{document}

プリセットの種類が増えた

luatexja-preset や pxchfon に追随するため、以下のプリセットを追加した。

  • ume:梅フォント。
  • yu-win10:游書体(Windows 10 搭載版)。
  • sourcehan: Source Han Serif(源ノ明朝)+ Source Han Sans(源ノ角ゴシック)、非サブセット版。
  • sourcehan-jp: Source Han Serif(源ノ明朝)+ Source Han Sans(源ノ角ゴシック)、地域別サブセット版。
  • noto: Noto Serif CJK JP + Noto Sans CJK JP(非サブセット版)。
  • noto-jp: Noto Serif JP + Noto Sans JP(地域別サブセット版)。

すなわち、BXJS クラスの jafont=yu-win10 などの設定が、XeLaTeX でも通るようになる。

% XeLaTeX文書; UTF-8
\documentclass[xelatex,ja=standard,a4paper,
  jafont=yu-win10]{bxjsarticle}
\begin{document}
{\TeX}\textbf{激アレ}% XeLaTeXで☃する場合は \jachar が必要
\sffamily \jachar{}\textbf{非アレ}\end{document}
源ノ系フォントに関する注意

基本的に、zxjafont はフォントを fontspec で指定する際にフォント名での指定<(ファイル名ではなく)を行う。従って、源ノ系フォントを利用する場合、フォント名での呼出に対応できる方法でフォントファイルを配置する必要がある。大概の TeX システムでは、OS にフォントをインストールすれば大丈夫である。$TEXMFLOCAL に配置したいという場合、Windows なら以下の記事も参考になるだろう。

Source Han Serif/Sans(非サブセット版)には 3 種類のファイル形式(Super-OTC 版、OTC 版、言語別 OTF 版)があるが、これらは同一のフォント名をもつので、全て同じプリセット sourcehan で指定することになる。地域別サブセット版だけは異なるフォント名(Source Han Serif/Sans JP)をもつため、別のプリセット sourcehan-jp を指定する。Noto フォントについても同様で、非サブセット(Noto 〜 CJK JP)の 3 つは noto、地域別サブセット(Noto 〜 JP)は noto-jp を指定する。

% XeLaTeX文書; UTF-8
\documentclass[xelatex,ja=standard,a4paper,
  jafont=sourcehan% Source Han Serif/Sans (JPじゃない奴)
% jafont=sourcehan-jp% Source Han Serif/Sans JP
% jafont=noto% Noto Serif/Sans CJK JP
% jafont=noto-jp% Noto Serif/Sans JP (CJKじゃない奴)
]{bxjsarticle}
\begin{document}
{\TeX}\textbf{激アレ}\sffamily \jachar{}\textbf{非アレ}\end{document}

2004JIS するか 90JIS するか

luatexja-preset に追随して字形選択のオプション*2を新設した。

  • jis2004: 2004JIS 字形を選択する。
  • 90jis: 90JIS(2000JIS)字形を選択する。
  • どちらも指定しない場合は、フォントの既定の字形が使われる。

BXJS クラスでの使用例を示す。
※“japaram={font={...}}”を指定すると、zxjafont に ... のオプションが渡される。この指定方法は LuaLaTeX でも全く同じ((LuaLaTeX では ... の部分のオプションが luatexja-preset に渡される。))であるため、エンジンオプションさえ lualatex に変えれば以下の例は LuaLaTeX でも通用する。

% XeLaTeX文書; UTF-8
\documentclass[xelatex,ja=standard,a4paper,
  jafont=sourcehan,japaram={font={jis2004}}]{bxjsarticle}
\begin{document}
巷で\textsf{}\textbf{葛飾区}\end{document}
% XeLaTeX文書; UTF-8
\documentclass[xelatex,ja=standard,a4paper,
  jafont=sourcehan,japaram={font={90jis}}]{bxjsarticle}
\begin{document}
巷で\textsf{}\textbf{葛飾区}\end{document}

その他諸々

  • 非推奨の古いプリセット指定(ipa-dx 等)に対して警告が出るようになった。

*1:ただし、ZXjafont は zxjatype を読み込まない(つまり“和文非対応”の)状態にも対応していて、この場合は通常の総称ファミリを設定対象とする。

*2:オプションの命名が不統一であるが、これも luatexja-preset に合わせたため。