アドベントカレンダー二日目 ― \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 | レジスタへの代入やマクロ定義のフック. |