以前に、TeX で「関数」をどう表現するか(特に「戻り値」をどう扱うか)について解説したことがある。その時に、「関数」として機能するマクロの書き方として次の 3 つを紹介した。
- 戻り値を受け取る変数をマクロの引数として渡す。
- 予め「戻り値を受け取る為の専用の変数」を用意する。
- 戻り値(を表現するトークン列)に完全展開されるマクロを作る。
実は、「関数」の実現方法にはもう 1 つあって、それが「CPS(継続渡しスタイル;Continuation Passing Style)を用いる」ことである。少しばかり変態的なので滅多に用いることはないが、しかしこの形式での実装が必要になる場合がある。それについて解説する。
簡単なプログラム課題
次の 3 つの関数 Foo()、Bar()、FooBar() を TeX のマクロ(名前を
\Foo
、\Bar
、\FooBar
とする)として実現せよ。「関数の実現方法」は自由とする。
- Foo(英字列) は引数の英字列の最初の 3 文字を返す。ただし 3 文字に満たない場合は
X
で埋める。例: Foo(textile
) =tex
;Foo(a
) =aXX
- Bar(英字列) は引数の英字列を逆順にした英字列を返す。例: Bar(
live
) =evil
- FooBar(英字列) は Foo() と Bar() の合成で、Foo(Bar(英字列)) を返す。例:FooBar(
overhaul
) =lua
このブログの熱心な読者*1ならば何の苦もなく済ませられるだろう。
\def\Foo#1{% Foo(#1) に完全展開される \xx@Foo@a#1XXX\relax % X を後ろに付ける } \def\xx@Foo@a#1#2#3#4\relax{% #1#2#3% 最初の3トークン } \def\Bar#1#2{% #1 := Bar(#2) に相当 \def\xx@tempa{}% \xx@Bar@a#2\relax \let#1\xx@tempa } \def\xx@Bar@a#1{% \ifx#1\relax\else \edef\xx@tempa{#1\xx@tempa}% \expandafter\xx@Bar@a \fi }
ここでは、\Foo
を 3 の方式、\Bar
を 1 の方式で実装した。この場合、これらの合成である \FooBar
は 1 の方式を用いて次のように書ける。
\def\FooBar#1#2{% #1 := FooBar(#2) に相当 \Bar\xx@tempb{#2}% % \xx@tempb を一回展開する必要がある \edef#1{\expandafter\Foo\expandafter{\xx@tempb}}% } %% テスト \FooBar\result{overhaul} \show\result %==> lua
さて、これだけなら極めて初歩的な問題であり、CPS とかが出てくる幕はない。
しかし、(当然予想される展開として、)「次の問題」が登場するわけである。
先の問題と同じ要件で、ただし今度は \FooBar
が完全展開可能であるように実装せよ。
(お楽しみはまた次回)
*1:ここではその実在性は議論しない。