[追記]勢いよく書いたら、疵<きず>だらけでした。修正の取消線があっちこっち。コード断片に取消線は付いてません(追加なので)が、shiroさんのご指摘で直しました。[/追記]
次のような入れ子になった例外処理を考えます。
try { try { コード(1) } catch(例外種別(1)) { 例外ハンドル(1) } 引き続くコード(2) } catch(例外種別(2)) { 例外ハンドル(2) } さらに引き続くコード(3)
これを、次のように書き換えても振る舞いは変わらないですよね。不格好なフラグがあったり、例外ハンドル(2) がコピー&ペーストになりますが、それは別にいいとします。
boolean handled = false; // 例外ハンドル(2) 以外では触らない try { コード(1) } catch(例外種別(1)) { try { 例外ハンドル(1) } catch(例外種別(2)) { 例外ハンドル(2) handled = true; } } if (!handled) { try { 引き続くコード(2) } catch(例外種別(2)) { 例外ハンドル(2) } } さらに引き続くコード(3)
それはそうとして、「/」を改行を表す記号に使って、行列を [1 0/ 0 1] のように書くことにします。縦ベクトル(2行1列の行列) [1/ 1] と、横ベクトル(1行2列の行列) [1 1] を、この順で掛け算すると、すべての成分が2である2×2行列 [2 2/ 2 2]*1 1である2×2行列 [1 1/ 1 1]になります。計算してみてください、そうなるでしょ。
1, 0がほんとの整数ではなくて、1がtrue, 0がfalseを表すと解釈し、足し算は論理AND/足し算は論理ORだとすると、1 + 1 = 1 となります。この解釈で計算すると、[1/ 1][1 1] = [1 1/ 1 1] となります。また、だけでなく、[1 1][1/ 1] = 1 も成立します。
ここでまた話は変わりますが、次の絵を眺めてください。
パイプ状の図形を変形していく様子です。簡略化すれば、アスキー図でも描けます -- ちょっと無理があるかぁ ^^;。
最初: \ / >ー< / \ 最後: -< >- × -< >-
さらに話は変わりますが、CPS(継続渡し方式)変換はご存知ですか。次のエントリーで取り上げたことがあります。
継続とは、ある時点より後で実行すべきプログラムだといっていいでしょう。それでは、継続の双対ってなんでしょうか? ある時点より前に実行すべき(あるいは、実行した)プログラムを“継続の双対”と思うことができます。では、余継続渡し方式(co-continuation passing style)ってなんか意味があるでしょうか。たいして意味はなさそうですが、考えてみる価値がゼロだとは思いません。
さて、何の関係もなさそうな話題を並べたのですが、これらは同一の話題です。同一の話題ですが、現れる分野や状況が違うのです。例外処理コードの書き換え、ブール値行列の計算、奇妙な図形の奇妙な変形、継続の双対、これら全ての背後には同じ原理と法則が控えています。統一的な説明が可能です。
不思議でしょ。面白いでしょ。僕はこういう話が楽しくて仕方ないのだけど、聞きたい/語りたいという人がいれば、パスタの会を企画しようかな。どうっすか?
*1:[追記]なんで、1×1 = 2 という計算をするかなー?>自分[/追記]