アドベントカレンダー二日目 ― \newif\ifnextyear \ifnextyeartrue
TeX & LaTeX Advent Calendar TeX で騒げ、TeX で笑え
この記事は,12/2 担当分です.< 12/1 は ZR さん | | 12/3 はk16.shikano さん>
周りは \expandafter を叫んだりする記事みたいなので,私はマイナーな(?)プリミティブである \aftergroup と LISP on TeX での利用法を一例としてあげようと思う.
そもそも \aftergroup ってなによ
黄色(LaTeX2e マクロ & プラス プログラミング基礎解説)*1にも載っていない*2が,TeX の強力なトークン展開の制御機能ひとつである.\aftergroup は現在のグループが終わった地点での動作をフックできる.使い方は,
\aftergroup<トークン>
で,トークンがグループ終了時に挿入される.複数指定したときは,指定した順序でそのすべてのトークンが挿入される.
これだけでは直感に欠けるので,コード例を.
{\aftergroup\show}\expandafter
実行結果は次のようになる.
$ tex test.tex
This is TeX, Version 3.1415926 (TeX Live 2012/dev/W32TeX)
encTeX v. Jun. 2004, reencoding enabled.
(./test.tex
> \expandafter=\expandafter.
l.1 {\aftergroup\show}\expandafter
? x
No pages of output.
Transcript written on test.log.グループの終わり,すなわち \expandafter の前に \show が挿入され,\show\expandafter となりこのような表示がなされる.
で,なにに使うの?
私も最初はよくわからなかった.しかし,TeX で言語処理系を実装するよくある状況*3では非常に有用なことがわかった.末尾呼び出しの最適化である.
末尾呼び出しの最適化とは?
末尾呼び出しの最適化とは,再帰呼び出しを安心してループのように使うために必要な最適化技術である.次の Scheme プログラムを例にして説明する.
(define (fact n acc) (if (= n 0) acc (fact (- n 1) (* acc n)))) (fact 10 1)
階乗を計算する末尾再帰になっているプログラムである.最適化しないナイーブな実装では,関数の呼び出しスタックは
[(fact 10 1)][(fact 9 10)][(fact 8 90)][(fact 7 720)].........[(fact 0 3628800)]
となる.ここで fact 関数の末尾での動作は,呼び出した関数の結果を返すだけというものであり,わざわざスタックを消費する必要がない.そんなときを検知して,スタックを潰すのが末尾呼び出しの最適化である.
で,\aftergroup との関連性は?
TeX でインタプリタを作成する場合,この呼び出しスタックをグループで表現するのがよくあるデザインである.グループのネストには限界があるので,末尾呼び出しの最適化は重要な課題となる*4.ここで,\aftergroup である.\aftergroup で「次にやるべき計算」*5をグループが終わったあとに挿入させるようにし,現在のグループを閉じてしまえば,不必要な要素をたたんで,新たな要素をスタックに積むという動作を実現することができる.
具体的に,LISP on Tex では \@@tco という名前のトークンに「次にやるべき計算」を束縛し,\aftergroup\@@tco とすることで末尾呼び出しを最適化している.
おまけ -- TeX におけるフック機能について
TeX には \aftergroup 以外にも,様々な場所をフックできるキチガイな言語である.一覧にしてみよう.
| \output | 出力ルーチンそのもの*6 |
| \par | 段落の間に挿入される.これを書き換えると,段落の終わりをフックできる.しかし,オリジナルの \par を実行しないと段落が変わらないため,初心者にはおすすめしない |
| \everypar | 段落の先頭に挿入されるトークンリスト.行頭に行番号を入れるなどのマクロは,これを使うと実装できる. |
| \everymath | 文章内の数式をはじめる時のフック. |
| \everydisplay | ディスプレイ数式のフック. |
| \everyvbox | 垂直ボックスの開始. |
| \everyhbox | 水平ボックスの開始. |
| \everycr | 表組みでの \cr の後ろ.TeX の表組みプリミティブである \halign と \valign を知ってないと使いづらいかもしれない. |
| \afterassignment | レジスタへの代入やマクロ定義のフック. |