あるいは 〜私の TeX プログラム変換環境〜
TeX & LaTeX Advent Calendar
群馬は TeX になりました。日本は TeX になりました。
明日開始、できればいいなあ。
TeX プログラムできたよ
非常に長い道のりであったが、ようやく普通の言語の「えるたそ」プログラム(eltaso.lua)から TeX(on LaTeX)の「えるたそ」パッケージ(tceltaso.sty)を作る作業が完了した。全体のプログラムを掲載しよう。
% tceltaso.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{tceltaso}
%% 変数定義
%(整数型)
\newcount\tclt@max
\newcount\tclt@j
\newcount\tclt@k
\newcount\tclt@ires % 戻り値
%(文字列型)
%\tclt@dm / \tclt@dc / \tclt@dx / \tclt@di
%\tclt@km / \tclt@kc / \tclt@kx
%\tclt@x
%\tclt@sres % 戻り値
%(配列型)
%\tclt@alpha@name/*
%\tclt@digit/*
%% \tclt@nameedef
\def\tclt@nameedef#1{\expandafter\edef\csname#1\endcsname}
%% \tclt@alpha@name/* : アルファベットの日本語読みの仮名表記
\tclt@j=0
\@for\tclt@x:=%
ぜっと,%
えー,びー,しー,でー,いー,えふ,じー,%
えいち,あい,じぇー,けー,える,えむ,えぬ,%
おー,ぴー,きゅー,あーる,えす,てぃー,ゆー,%
ぶい,だぶりゅー,えっくす,わい%
\do{%
\tclt@nameedef{tclt@alpha@name/\the\tclt@j}{\tclt@x}%
\advance\tclt@j 1
}
%% \tclt@digit/* : 漢数字(1〜9)
\tclt@j=0
\@for\tclt@x:=,一,二,三,四,五,六,七,八,九\do{%
\tclt@nameedef{tclt@digit/\the\tclt@j}{\tclt@x}%
\advance\tclt@j 1
}
%% \tclt@knumeral{<整数n>}
% 整数n(1〜9999)の漢数字表記.
\def\tclt@knumeral#1{%
\tclt@split@digit{#1}%
% \edef\tclt@km{\tclt@sres} でもよいが \let が使える
\tclt@knum@pos{\tclt@dm}{千}\let\tclt@km\tclt@sres
\tclt@knum@pos{\tclt@dc}{百}\let\tclt@kc\tclt@sres
\tclt@knum@pos{\tclt@dx}{十}\let\tclt@kx\tclt@sres
\edef\tclt@sres{\tclt@km\tclt@kc\tclt@kx\@nameuse{tclt@digit/\tclt@di}}%
}
\def\tclt@split@digit#1{%
\edef\tclt@x{\noexpand\tclt@split@digit@a\the\tclt@j%
\noexpand\tclt@nil}%
\tclt@x
}
\def\tclt@split@digit@a#1\tclt@nil{%
\tclt@split@digit@b#1/000\tclt@nil
}
\def\tclt@split@digit@b#1#2#3#4#5#6\tclt@nil{%
\tclt@split@digit@c#1#2#3#4#5\tclt@nil
}
\def\tclt@split@digit@c#1/#2\tclt@nil{%
\tclt@split@digit@d#2#1%
}
\def\tclt@split@digit@d#1#2#3#4{%
\def\tclt@dm{#1}%
\def\tclt@dc{#2}%
\def\tclt@dx{#3}%
\def\tclt@di{#4}%
}
\def\tclt@knum@pos#1#2{%
\ifnum#1=0 \def\tclt@sres{}%
\else\ifnum#1=1 \def\tclt@sres{#2}%
\else \edef\tclt@sres{\@nameuse{tclt@digit/#1}#2}%
\fi\fi
}
%% \tclt@eltaso@name{<整数n>}
% 整数n(1〜9999)に対する「えるたそ名」.
\def\tclt@eltaso@name#1{%
\tclt@knumeral{#1}%
\tclt@remainder{#1}{26}%
\edef\tclt@sres{\tclt@sres 反田\@nameuse{tclt@alpha@name/\the\tclt@ires}}%
}
\def\tclt@remainder#1#2{%
\tclt@k=#1\relax
\divide\tclt@k#2\relax
\multiply\tclt@k-#2\relax
\advance\tclt@k#1\relax
\tclt@ires=\tclt@k
}
%%(公開) \eltaso{<整数n>}
% 整数n(1〜9999の範囲)に対して, 1からnの各整数
% に対する「えるたそ名」を出力する.
\newcommand*\eltaso[1]{%
\tclt@max=#1\relax
\ifnum\tclt@max>9999 \tclt@max=9999 \fi
\par
\tclt@j=0
\@whilenum\tclt@j<\tclt@max \do{%
\advance\tclt@j 1
\tclt@eltaso@name{\tclt@j}%
\tclt@sres\par
}%
}
\endinputテスト用の文書と、それをコンパイルした出力結果を載せる。正常に動作していることが判る。
% pLaTeX 文書 \documentclass[a4paper]{jsarticle} \usepackage{tceltaso} \begin{document} \eltaso{1000} \end{document}

* * *
ところで、私が TeX でプログラミングするときに、本当にこんな面倒な手順を一々踏んでいるのかというと、実際にはそうではない。単純なロジックのプログラムなら直接 TeX で書くことが多い。しかし、ある程度複雑なロジックをもつプログラムの場合、
- 普通の言語で「TeX の動作に近いプログラム」を作り、それが正しく動作することを確認する。(動作しなければそのプログラムをデバッグする。)
- そのプログラムを「ある程度機械的な」作業で TeX のプログラムに書き換える。
という作業を実際にやっている。例えば、「マンデルブロートなアレ」では、計算の部分(各点の色番号を求める)は Perl で書いたコードから「変換」したものを使っている。また、極めて複雑なロジックをもつ「AtCoder なアレ」とか「ほむほむなアレ」とかは、このシリーズで解説した一連の手順をかなり忠実に実行している。
この手順において一番大事な利点は、
ロジックのコーディングを、TeX でなく(もっと書き易い)他の言語で行う
というこである。かなり TeX のプログラミングに慣れた人であっても、やっぱり Lua とか Perl とかの方がずっと書き易くデバッグし易いという事実は変わらない。だから一見遠回りに見えても、他の言語を経由させることで労力を節約できると私は感じている。また、敢えて読みにくい(また慣れていない)TeX でロジックを組むことでバグが入ったり不恰好なコードになることを防ぐことができるだろう。
一連の解説において、また、一般的な TeX プログラミングの知識として次のことを説明した。
- 関数や配列という「ほんの少し高度な」概念の、TeX での扱い。これらは「まともな」プログラムを書くのに不可欠だが、まとまった説明をあまり見かけない。
- 名前空間やコーディングスタイル等の「慣習」の話。こういうことは初級者の段階から気を留めておいた方がいいと思っている。少なくても何の考えも無く Knuth のスタイルを真似るというのは推奨できない。
普通の言語から変換するという方法だけで、どんな TeX プログラムでも作れるわけではないことに注意して欲しい。そもそも TeX 本来の機能である「複雑な組版」を目的とするマクロは、そもそも TeX 以外の普通の言語で書けないだろう。また、TeX 機能のうちここで示した過程で使われるのはごく一部なので、その知識だけでは LaTeX の実装コード(source2e)を読めるようにはならない。従って、TeX/LaTeX 上の「まともな」機能拡張を行う(文書クラスファイルの実装など)には「まともな」TeX on LaTeX の学習が必須であることに変わりないことを注意しておきたい。それでも、とにかく TeX で(ネタ的なことであっても)自分の思うことが実現できるという経験は、「まともな」TeX プログラムを書く際にも役立つことも間違いない。