ラムダ計算インタプリタを作ってみた

JavaScriptで学ぶ・プログラマのためのラムダ計算 - 檜山正幸のキマイラ飼育記 (はてなBlog) を見てたらラムダ計算のインタプリタを作りたくなってしまった。JavaScript で適当に作成。
http://v.takeuchi.googlepages.com/lambda.html
例えば 問題集編 にある

(λx.1)[(λx.[x(x)])(λx.[x(x)])]

(λx.1)((λx.x x) (λx.x x))

とすると実行できます。
別の例として、チャーチ数を使って 3 * 4 を計算してみます。

(λm.λn. m ((λm.λn.λs.λz. m s (n s z)) n) (λs.λz.z)) (λs.λz.s(s(s(z)))) (λs.λz.s(s(s(s(z)))))

を入力して適当にクリックしていくと

λs.λz.s (s (s (s (s (s (s (s (s (s (s (s z)))))))))))

になります。
Firefox でしか動作確認してないので IE では動かないかも…。しかも引数は一つしかとれないし、演算子も使えません。
TAPL のページとかには、もっとまともな実装があるのでそちらも。
追記: Opera に対応した…かも。
追記2: 下線が表示されなかったりカーソルが変化しなかったりするなあ。むむ。

おもしろおかしい

一度目に「くだらない揚げ足取りだなぁ」と思って読んだコメントでも、
「おもしろおかしい」モデがついてからもう一度見ると本当におもしろおかしく見えてしまうから不思議。


#世の発言のすべてに「おもしろおかしい」モデを付ければ、争いなんてなくなるんじゃなかろうか。
#「女性は子供を産むための機械 (スコア:2, おもしろおかしい)」とか

http://slashdot.jp/linux/comments.pl?sid=350764&cid=1106758

噴いた。すばらしい洞察。

「言葉狩り」?

柳沢さんへの批判を「言葉狩り」と言う人がいるが、俺の感覚ではこういう時に「言葉狩り」というのは違和感を感じるなあ。
言葉狩り」というのは特定の用語(特に差別用語)の使用を禁じることであって、今回のケースでは「言葉尻を捕らえる」とか「揚げ足を取る」とかの表現のほうがしっくりくる。

オブジェクトと連想配列

JavaScript の配列と連想配列の違い - IT戦記
連想配列で for in を使う時でも

Object.prototype.hoge = "Foo";

とかしてたらまずいような気がしますがどうでしょう。
じゃあどうしたらいいんだろう。ループ内で hasOwnProperty とかでチェック?

オブジェクト=連想配列というのはシンプルで強力な機構ではあるのですが、連想配列として使う場合には使いづらいのではないかとよく思います。
例えば

function assocEmptyHash(key){
  var hash = {};
  return hash[key];
}

という関数を考えます。これを Ruby で書けば

def assoc_empty_hash(key)
  hash = {}
  hash[key]
end

となるでしょう。Ruby 版は常に nil を返します。しかし JavaScript のほうは、引数がたまたま 'valueOf' だったりした場合 Object.prototype.valueOf が返ってきてしまいます。
JavaScript で同じようにするには

function assocEmptyHash(key){
  var hash = {};
  if (hash.hasOwnProperty(key)){
    return hash[key];
  }
}

とするとうまく…駄目だ。hash.hasOwnProperty("__proto__") が true になってしまいます。しかも __proto__ に値をセットすると for in を使うときにも色々まずいことが起こります。てなわけで、キーをそのまま使うのはよくないみたいです。なので、キーに適当な文字列を付け加えて

hash[key + ":HASH_KEY"];

とし、セットするときにも同じ文字列を付け加えればちゃんと動きます(多分)。
とはいえ、ハッシュのアクセスのたびにこんなことするわけにもいかないので、ラッパーを作ったり使ったりするでしょう。そう考えると、最初から Object とは別の連想配列があったほうがいいなと思ってしまうのです。

JavaScript のオブジェクト

Ruby の object_id を JavaScript でも使えないかなと思って書いてみた。let が無いのがもどかしい。

Object.prototype.getObjectId = function(){
  var object_id = 1;
  return function(){
    if (this.hasOwnProperty('__object_id')){
      return this.__object_id;
    }else{
      object_id++;
      this.__object_id = object_id;
      return this.__object_id;
    }
  };
}();

これを使ってみると、

(1).getObjectId();     // => 2
(1).getObjectId();     // => 3

foo = 100;
foo.getObjectId();     // => 4
foo.getObjectId();     // => 5

bar = Object(200); 
bar.getObjectId();     // => 6
bar.getObjectId();     // => 6

hoge = "HOGE";
hoge.getObjectId();    // => 7
hoge.getObjectId();    // => 8

fuga = Object("FUGA");
fuga.getObjectId();    // => 9
fuga.getObjectId();    // => 9

piyo = [];
piyo.getObjectId();    // => 10
piyo.getObjectId();    // => 10

hogera = Object(piyo);
hogera.getObjectId();  // => 10

オブジェクトとそうでないものとの違いがはっきりわかって面白い。でもこの「オブジェクトに自動変換される」的な挙動は大キライさ。