マクロツイーター

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

LaTeX を破壊する話

先日の記事では、「TeX 言語がフリーフォーマットでない」ことを述べた。その時に紹介した「例のスライド」では、TeX 言語の(プログラミング言語としての)特徴として次の 3 つを挙げている。

  • フリーフォーマットではない
  • ローカル変数はない
  • 「関数」はない

さらに、13 枚目のスライドで「ローカル変数はない」について述べられている。

ここでは「\xx@a など、識別子をつけて名前が被らないように工夫する必要」があることが主張されている。ところで、前の記事では、スライド 7 枚目に掲載されている「階乗を求めるプログラム」(マクロ \factorial)が「フリーフォーマットではない」という特質に関する「バグ例」になっていることを解説した。それを踏まえた上で改めてそのコードを見てみると、先頭での整数変数の宣言において、\@m\@k という「識別子のない、極めて短い」名前の制御綴が使われていることに気付く。

\newcount\@m
\newcount\@k

となると、これは「ローカル変数はない」という特質についても「バグ例」にもなっていることが容易に想像される。事実その通りで、\@mLaTeX では*1既に定義されている制御綴である。すなわち、このプログラムは

LaTeX を破壊している。

LaTeX を破壊して爆発させる話

LaTeX が破壊されたことを示すには、それが原因で「爆発」する――すなわち、そのコードを実行した後で、LaTeX が「意味不明な異常動作」を起こす――例を作る必要がある。残念ながら、元のプログラムのままではあまり上手い例を作るのが難しかったので、ここでは、少し書き換えたコードについて、スッキリと(?)爆発する例を示すことにする。

\documentclass{article}
\makeatletter
\newcount\@m
\newcount\@k
% \@k と \@m の使用を入れ替えた
\def\factorial#1{%
  \@k = 1 \relax
  \@m = 1 \relax
  \@whilenum \@k < #1 \do {%
    \advance \@k 1 \relax
    \multiply \@m \@k \relax
  }
  \the\@m
}
\makeatother
\begin{document}
The factorial of 10 is \factorial{10}.

Blah blah blah.
Blah blah blah.

% 何の変哲もない別行数式
\[ a + b = c \]

\end{document}

上のソースコードでは、元の \factorial の定義で \@m\@k の使用を入れ替えたものを書いている。その上で、\factorial を実行して、その後に文章が続いて、さらにその後に非常に単純な別行立て数式が書かれている。これをコンパイルすると、数式の箇所で意味不明な「爆発」が発生する。

! Dimension too large.
\nointerlineskip ->\prevdepth -\@m \p@ 
                                       
l.23 \[
        a + b = c \]
?

ここでは、破壊されているのを知った上で爆発を見ているので当たり前のように見えるかも知れないが、現実に(つまり LaTeX が破壊されていることを知らずに)この状況に遭遇したらどうだろう。謎の爆発の原因を突き止める作業は困難を極めることは容易に想像がつく。((実は今のケースだと、エラー行表示「\nointerlineskip ->\prevdepth -\@m \p@」をよく見ると、そこに(自分が書いたのでない)\@m があるので、それが制御綴の衝突に気付く手がかりになるだろう。しかし、もっと解りにくい状況になる可能性は幾らでもある。))

「自分は大丈夫」と思うなかれ

ここまで読んで、「いや、自分は制御綴が定義済かをちゃんと調べているので、短い名前の制御綴を使ってても大丈夫」と思った人がいるかも知れない。しかし残念ながらその自分の注意力への信頼は当てにならないのかも知れない。実は、この「\@m の再定義」は(TeX プログラミングによる)「LaTeX 破壊事例」の中でも頻出するものの一つである。ネットに公開されている TeX コード*2でも \@m を書き潰しているものは頻繁に見かける。それを書いた人は LaTeX を破壊したかったのであろうか。無論違うだろう。それを考えると、「注意力に依拠せず常にユニークな名前の制御綴を使う」ことに徹した方が賢明であることが解るだろう。

*1:plain TeX でも。

*2:特にバグ例であることが示されていない、つまり書いた本人が恐らく「正しい」と判断しているコード。