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

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

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

参照用 記事

セミナー補足:関数コードの実行エンジン

セミナー終わって、みんなでご飯食べて、太田君の補習をしていたら、(僕にしては)だいぶ遅くなってしまったのですが、なにしろ事前にテンションあげていたので、すぐに眠れない。んでまー、本日の「Eの、いろいろな定式化」について補足します。(ほぼ参加者にしか通用しない話で申し訳ないのですが。)

fが2引数関数で、f^ は、fの関数コード、ただしパラメータは残ってないとする。

  1. E(f^, a, b) = f(a, b) -- Eは3引数
  2. E(f^, [a, b]) = f(a, b) -- Eは2引数、第2引数はタプル
  3. E([f^, a, b]) = f(a, b) -- Eはタプル1引数、タプルは3項
  4. E([f^, [a, b]]) = f(a, b) -- Eはタプル1引数、タプルは2項だが入れ子

関数コードf^とは、関数の計算手順を刷り込んだデータです。Eは、Exec, Eval, Engineなどの頭文字Eをとったもので、関数コードf^(ある種の機械語コード)と“もとの関数fに渡すべき引数”を引数に取り、もとの関数fの計算結果を再現する関数です。

上の引用は、関数コード実行(評価)エンジンEの作り方(つうか、インターフェース)が色々あるよね、という話。「関数コードの記述言語=エンジンEの機械語」が人間可読なほどに高水準で、実はラムダ式(小さなラムダ式)だとすると、fが足し算のケースでは:

  1. E(λ(x, y).(x + y), a, b) = <x, y| x + y>(a, b)
  2. E(λ(x, y).(x + y), [a, b]) = <x, y| x + y>(a, b)
  3. E([λ(x, y).(x + y), a, b]) = <x, y| x + y>(a, b)
  4. E([λ(x, y).(x + y), [a, b]]) = <x, y| x + y>(a, b)

等号の右辺は関数そのものなので大きなラムダ式で記述しました。

同じEでは区別できないので、上から順に、Apply, Exec, Eval1, Eval2と名付けましょう。それらをJavaScriptで書いてみると:

/* 関数コードと、引数並び(タプル、リスト)を受け取って、
 * もとの関数の計算を再現する
 */ 
function Apply(fun, args) {
  return fun.apply(null, args);
}

/* 関数コードと、その関数コードに埋め込まれてない任意の引数を受け取って、
 * もとの関数の計算を再現する
 * Exec自体は可変引数
 */
function Exec(fun /* ... */) {
  var args = new Array();
  for (var i = 1; i < arguments.length; i++) {
    args[i-1] = arguments[i];
  }
  return fun.apply(null, args);
}

/* 関数コードと、引数をそのまま並べたタプル(リスト)を受け取って
 * もとの関数の計算を再現する
 */ 
function Eval1(funArgs) {
  var fun = funArgs.shift();
  var args = funArgs;
  return Apply(fun, args);
}

/* 関数コードと、引数並びの組み(2項のタプル、リスト)を受け取って
 * もとの関数の計算を再現する
 */ 
function Eval2(funArgs) {
  var fun = funArgs[0];
  var args = funArgs[1];
  return Apply(fun, args);
}

[追記 date=""]ウゲッ:

  var fun = funArgs[0];
  funArgs.shift();

かっちょ悪すぎ。恥ずかしいので、コソッと直しました。[/追記]

実行例:

js> Apply(function(x, y){return x + y}, [2, 3])
5
js> Exec(function(x, y){return x + y}, 2, 3)
5
js> Eval1([function(x, y){return x + y}, 2, 3])
5
js> Eval2([function(x, y){return x + y}, [2, 3]])
5
js> 

もともとJavaScriptにあるevalは、まず文字列をパーザーにかけて、Eval1やEval2が受け取っているような構造を作ってから評価していることになります。

さてところで、上で作ったようなApply, Exec, Evalなどは、“外の世界”にリアルに存在している実体としての「関数コード実行エンジン」を、JavaScriptインタプリタというマイクロコスモス内にミニチュアとして実現したものです。スノーグローブ内のお家が、本物のお家ではなくても、やっぱりお家とみなせるのと同じように、Applyなどは本物(例えばハードウェア)の実行エンジンではないけど、やっぱり実行エンジンとみなせるのです。