マクロツイーター

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

TeX で CPS したいとき(1)

以前に、TeX で「関数」をどう表現するか(特に「戻り値」をどう扱うか)について解説したことがある。その時に、「関数」として機能するマクロの書き方として次の 3 つを紹介した。

  1. 戻り値を受け取る変数をマクロの引数として渡す。
  2. 予め「戻り値を受け取る為の専用の変数」を用意する。
  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:ここではその実在性は議論しない。