マクロツイーター

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

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

あるいは 〜私の TeX プログラム変換環境〜

ステップ 1 : 仕様を決める

早速プログラムの変換を始めたいところだが、その前に。

パッケージの名前を決める

すなわち \eltaso の実装コードを書きこむ .sty ファイルの名前を決める。ここでは tceltaso と命名しよう。*1そうすると、ファイル名は tceltaso.sty となり、このパッケージを利用した文書は例えば次のようになるだろう。

\documentclass[a4paper]{jsarticle}
\usepackage{tceltaso}
\begin{docucment}
\eltaso{1000}
\end{document}

なお、元ネタの著者がやっているように、LaTeX 文書のプレアンブル部に(\makeatletter\makeatother で囲って)TeX (on LaTeX)のコードを書くことも可能である。((本体部(\begin{documetn} の後)でも TeX コードの記述は可能で、実際にそういう「TeX on LaTeX 文書」を作成する人もいる。))しかし、私は原則として、TeX(on LaTeX)コードは文書中には書かずに別にパッケージファイルを作り、文書中で \usepackage する、というスタイルに徹している。それは何故かと言うと、「機能のマークアップのコードと機能の実装のコードを分離するため」云々も多少はあるが、ぶっちゃけた話、

TeXLaTeX のコードが一つのテキストの中で混在するのがとても嫌

というのが一番の理由だと自分で理解している。例えていうと、LuaLaTeX では luacode パッケージを利用する(あるいは \directlua プリミティブを直接使う)ことで Lua コードを LaTeX 文書に混在して記述することが可能であるが、実際には異なる言語が混在することを嫌って Lua コードを別のファイルに書く人が多い、というのと同様の話である。TeXLaTeX は異なる言語である*2ので、それぞれを別のテキストに分けておきたいのである。

名前空間の識別子を決める

名前空間」というのは、制御綴の名前の付け方に関する慣習のことである。(だから TeXLaTeX の正式な用語・概念ではない*3)具体的には、「名前空間を xx にする」というのは、自分が定義して使う制御綴の名雨を全て \xx@〜 から始めることを意味する。ちなみに、この残りの部分は単語を @ で区切ったものをおく(例えば \xx@prove@theorem 等((要するに C言語Perl_ に相当するのが @ である。他言語と同様、何らかの特別な用途で @@ と重ねたり末尾に @@@ を付けたりすることもある。(先頭は名前空間名だから付けない。)例えば私は、「hoge」という文字列定数を表すマクロには \xx@@hoge という名前を付けている。なお、どこかの Knuth 氏のような @ の付け方は、よい子はマネしないように。)))。

私の場合、名前空間識別子には、パッケージ名を省略した 4 文字の英字の列を用いることにしている。ここではパッケージ名が tceltaso なので識別子を tclt としよう。*4

名前空間とかいうと何やら難しい概念のように聞こえるが、要するに「制御綴を全部 \tclt@〜 にした」ということに過ぎない。

補足:「名前空間」を設定することの利点

実際のところ、(著名な)TeX プログラマの全てが名前空間の慣習を取り入れているわけではない(最近かなり増えているが)。しかし、私は「名前空間」を用いることを強く奨めている。何故なら、次のような大きなメリットがあるからである。

ユニークな名前を都度考える(或いは制御綴名の衝突に対処する)手間が省ける

TeX には(静的スコープの)ローカル変数という概念がないので、あるプログラムが使用する(原理的には)全ての制御綴について、「その制御綴を他の人が使った(使っている)場合に不具合が出ない」ように細心の注意を払う必要がある。例えば、\@m という制御綴は LaTeX の内部コードで使われている。((\mathchardef\@m=1000 という定義で、定数の 1000 を表す。))だから、もしこの制御綴を自分が別の意味で使うならば、少なくとも「局所的な定義」でなければならないし、そうしたとしても \@m の定義を変えている間は LaTeX の(内部)命令を呼び出すことはできない。((つまり、安全に実行できるのは TeX のプリミティブくらいしかない。あと、何らかの出力を行うと出力ルーチンが稼動する可能性があるのでそれもできない。要するに全然得策でないので \@m を使うのは諦めるべきである。))

しかし、現実的には、「全ての」制御綴りについてこのような配慮を行うことは労力的に無理があり、場合にとっては完全に不可能である。だから実際には「ある程度ユニークな名前だったら衝突しない!」と割り切って考えるという運用が行われている。例えば、\@name は「衝突するかも知れない」が、\get@eltaso@name だったら「ある程度ユニーク」だから「衝突しない」と見做すわけである。ただそれでも新しい制御綴を用意しようとする度に「ある程度ユニークな」名前を考え出すのは面倒である。ループカウンタ等は単なる \@i とか \@j とかにしたいが、流石にこれは「衝突しうる」と見做さざるを得ないだろう。*5

名前空間を使うことでこうした「精神的な面倒」から解放される。つまり、「\tctl@〜 で始まる制御綴は全部衝突しない!」と見做すことで、衝突の心配を全く考えなくてよくなる。ループカウンタは \tctl@i で全く問題がない。もし万が一名前空間の識別子が他のパッケージと重複していたことが判明したとしても、ほとんどの場合、ソースファイル中の「tctl@」を一斉に他の識別子に置換すれば解決する。

公開命令の仕様をちゃんと決める

公開命令というのは、当該のパッケージを読み込むことで(laTeX の)命令として LaTeX 文書中で使えるようになるマクロのことで、tceltaso パッケージの場合は \eltaso 命令(先に示した例文書を参照)ということになる。この命令の仕様を「厳密に」定義したい。といっても「1→一反田えー、2→二反田びー、……」という対応は、このプログラムに取り組む人なら理解しているはずなので、これを単に「えるたそ名」と呼んで済ませることにする。

  • \eltaso{<整数n>} : (n は 1〜9999 の範囲;整数は内部値((「内部値」とは、「42」とか「"CAFE」とかの数字で表記されたもの以外の、「\@tempcnta」「\tw@(定数 2)」のような整数の表現。LaTeX のコードで内部値が用いられることは滅多にないが、例えば「\value{<LaTeXカウンタ名>}」は内部値である。))でもよい)
    1からnまでの整数について、その「えるたそ名」を順に出力する。最初の出力の直前および各出力の直後で改段落(\par)を行う。

上述の仕様では、\eltaso{3} は以下の LaTeX ソースと同じ出力を行うことになる。

\par 一反田えー\par 二反田びー\par 三反田しー\par

「前後で改段落を行う」というのは当たり前のように思えるかも知れないが、例えば次のような「別の可能性」もある。

par\noindent 一反田えー\\二反田びー\\三反田しー\par

この仕様では改段落(\par)の代わりに強制改行(\\)を行っている。2 行目以降には段落下げの空きが入らないのでそれに合わせて先頭行に入る段落下げを除去している。どちらにするかは設計者が決めなければならない。ここでは、元ネタの実装仕様に合わせたいという理由で前者を採用した。((実は後者には大きな欠点がある。TeX の処理では、現在処理中の段落の内容は一旦メモリに格納されるので、単一の段落の内容があまりに巨大になるとメモリ不足に陥ってしまう。かなり昔の処理系だと、後者の仕様で \eltaso{9999} を実行するとメモリ不足のエラーが起こると思われる。))

以上で、「何を作ればよいか」が明確になった。次はいよいよプログラムの変換作業を始めよう。

*1:「tc〜」は “TeX Comedian”(TeX芸人)の略で、ネタ的なパッケージの名前につける接頭辞として私が最近使い始めている。ちなみに「真面目な」パッケージには「bx〜」(エンジン汎用)、「px〜」(pTeX 系用)、「zx〜」(XeTeX 用)で始まる名前を用いている。

*2:マジです。

*3:多くのプログラム言語には名前空間を扱うための何らかの機構があるが、TeX にはそれがない。

*4:パッケージ名の先頭 2 文字(tc、bx、px、zx)をそのまま識別子の先頭 2 文字としている。従って、4 文字が偶然何かの英単語と一致する(つまり「名前空間」が欠落した制御綴名と衝突する)可能性が無くなる。

*5:念のため繰り返すが、「衝突しうる」と見做される制御綴を使うならば、必ず「衝突した場合にも、自分のコードも相手のコードも正しく動く」ことを保証しなければならない。そうしないというのは、要するに、潜在的なバグを放置し続けているということで、あってはならないことである。