第2回プログラム等価性クイズ

たまには自分の研究に関係がある(かつ専門家以外にもわかる)話題ということで、プログラム等価性(program equivalence)に関して書いてみます。プログラム等価性というのは「二つ(ないし二つ以上)のプログラムが等価」という性質のことで(そのまんまやんけ!)、「等価」の粒度によって様々な「プログラム等価性」があります。その中でも

任意の文脈(プログラムの「使い方」や「呼び出し方」のようなもの)Cにおいて、C[P](文脈CにおいてプログラムPを実行する計算)が停止するならば、C[Q]も停止するし、逆も成り立つ

というプログラム等価性を文脈等価性(contextual equivalence)と呼ぶことがあります。

例えばJavascript*1の命令型言語で、

function f1(g) {
  var x = 1;
  g();
  return x+1;
}

という関数f1と、

function f2(g) {
  var x = 2;
  g();
  return x;
}

という関数f2は、(reflectionっぽい機能とかを無視すれば)文脈等価になります。どんな呼び出し方をしても、f1とf2は両方とも停止する(あるいは両方とも停止しない)と考えられるからです。停止する・しないという定義なので、実行速度などは無視します。また、関数呼び出しのためのスタックを含め、メモリは無限にあると仮定します。

一方、例えば

function f3(g) {
  var x = 1;
  g(x);
  return x+1;
}

という関数f3と、

function f4(g) {
  var x = 2;
  g(x);
  return x;
}

という関数f4は文脈等価になりません。文脈C[f]として

function d() { d(); } /* 停止しない関数 */
function g(x) {
  if (x > 1) { d(); }
}
f(g);

みたいな使い方を考えると、C[f3]は停止するのにC[f4]は停止しないからです。

…などというつまらない能書きはこれぐらいにして、さて、

function f5(g) {
  function d() { d(); } /* ←追記3の理由で追加・修正 */
  g(d);
}

なる関数f5と、

function f6(g) {
  var y = 0;
  function h() { y = 1; }
  g(h);
  function d() { d(); } /* ←追記3の理由で追加・修正 */
  if (y == 1) { d(); }
}

なる関数f6は文脈等価でしょうか?(dは上と同様、呼び出されると停止しない関数とします。) つまり、C[f5]は停止するけれどもC[f6]は停止しない(ないしその逆)、という文脈Cは存在するでしょうか? 繰り返しになりますが、ここでは停止性だけを考えているので、コンソールやネットワークへの入出力とか、実行効率とかは「等価」の定義に含まれていません。(なお、上のf5, f6は私が考えたわけではなく、昔からよく知られている例です。念のため。)

答えはCMの後で。

ヒント(?): 前回(3年以上前!)のクイズ

shiroさんからツッコミが入ったので追記:exceptionとか(C言語でいう)exitとか、setjmp/longjmpとかcall/ccとかも無しでお願いします(あると簡単すぎるので)。我田引水すると、こういう「アレは無し」「コレも無し」という追記を何度もしないためには、計算モデルの数学的定義が必要になります。:-) 知っている方のために、ここでは参照(reference)入りの型無しλ計算あたりを考えています。

追記2:関数を==で比較するのも(簡単すぎるので)禁止。やはり言語を定義せずに出題するのは無理があったでしょうかorz。非形式的には、ネスト可能な第一級関数と静的スコープの変数と破壊的代入と整数演算からなる言語、ということで…

追記3:dを上書きするのもやめてください。;_; 問題を修正しました。最初からML(ただし例外は無し)で書けばいいのに無理してJavascriptなんか使うから…>自分

*1:本当のJavascriptは自信がないので…