このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

CPS(継続渡し方式)変換で裏返る理由

昨日CPS(継続渡し方式)への変換を話題にしました。

最近CPSに凝っているらしいid:higeponさんが:

人間の皮が裏返る。外が内、内が外。みたいなイメージでしょうか。

と書いてます(CPSで思考する - higepon blog)。この裏返る感じを説明してみたいと思います。

プログラムが、3つの部分A, B, Cから構成されているとします。

A;
B;
C;

A, B, Cは単一の文ではなくて大きなブロックでもかまいません。ブロックAの計算結果は変数aに代入され、ブロックBの計算結果は変数bに代入されるとしましょう。それぞれのブロックを関数化すると、次のようになります。

a = A();
b = B(a);
c = C(b);

変数cの値がプログラム全体の結果となります。

中間変数a, bは不要なので除去すると:

c = C(B(A()));

結局、プログラム全体の機能は、C(B(A()))) という入れ子の関数呼び出しで表現できます。ここで、入れ子の内側から見ると、A, B, C の順序であることに注意してください。


次にCPS(継続渡し方式)を使ってみましょう。関数BをCPS変換したものをB*とします。Bに引き続く処理はCなので、CをB*に渡す形に変換すれば:

a = A();
c = B*(C)(a);

関数B*(C)を一時的にFと置くと:

a = A();
c = F(a);

今度は、関数AのCPS変換をA*として:

c = A*(F)();

Fをもとに戻して、

c = A*(B*(C))();

形を整えるために、何もしない関数をIとして、CのCPS変換C*を使って、

c = A*(B*(C*(I)))();

出来上がった式 A*(B*(C*(I)))() は、入れ子の内側からの順序が C, B, A になっています。関数結合(合成)を「・」で表すともっと見やすくて:

  • 単純な関数化:(C・B・A)()
  • CPSへの変換:(A*・B*・C*)(I)()

素直な関数化に対してCPS変換は確かに裏返ってます

この事実の背景は、CPS変換(実は米田埋め込み)が反変関手だということです。