以下を見て私も作りたくなったので。
で、出来上がったのがこちら。
var curry = function(f){
var fs = f.toString();
var op = fs.indexOf('(');
var cp = fs.indexOf(')');
var ob = fs.indexOf('{');
var cb = fs.lastIndexOf('}');
var args = fs.substr(op+1,cp-op-1).split(/,\s*/);
if (!args.length) return f; // 具がないのはそのまま返す
var curried = new Function(args.pop(), fs.substr(ob+1,cb-ob-1));
while (args.length){
curried = new Function(args.pop(), 'return ' +
curried.toString().replace(/\n/,''));
// .toString().replace(/\n/,'') は Opera対策
}
return curried;
};
Firefox2.0.0.12, Safari 3.04, Opera 9.25 で動作確認済み。IEは読者まかせ:)
見ての通り、具(関数)を一端文字列としてバラして煮込み直している。考えとしては、
JavaScriptでカリー化 - 檜山正幸のキマイラ飼育記うーん、テキストつぎはぎは、やっぱダサイな -- いいんか? これで。
に近いのだけど、eval()も正規表現も(split以外では)使わないところが隠し味。
ダサいといえばダサいのだけど、こうするには理由が3つある。
- 純粋にcurry化された関数が欲しかった。これは
curry(function(a,b,c){ return a+b+c })(1)(2)(3)のみをOKとし、curry(function(a,b,c){ return a+b+c })(1)(2,3)はいらない、という意味。 - curry化されたものを文字列に戻した時に、一目見てcurry化されたというのがわかるようにしたかった。例えば
curry(function(a,b,c){ return a+b+c }).toString()は、私のrecipeだとfunction anonymous(a) { return function anonymous(b) { return function anonymous(c) { return a + b + c; }; }; }となるけど、nanto版だと
function (_0, _1, _2) { return f.apply(this, arguments); }となって原型を留めていない。
- 実行速度。nanto版は、部分適用の都度、関数を作り直しているので遅そう。
最後のものに関しては、ベンチマークを取ってみた。
function cook_curry(recipe, times){
return function(){
var spice = function(a,b,c){ return 1 };
for (var curry = recipe(spice), i = 0; i < times; i++){
for (var f = curry(i), j = 0; j < times; j++){
for (var g = f(j), k = 0; k < times; k++){
var h = g(k);
if (h == 1) continue;
alert('whoa!');
return;
}
}
}
}
}
というやり方で、三重に調理した場合の時間を測るのである。
以下、実際に計測。
Run with 3 iterations:
203回のiterationで、いずれも20倍ぐらい差が出た。
これでHaskellとも勝負できる...か!?
Dan the Curry Cooker
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。