で、その pipe でエラトステネスの篩をやってみると以下のような感じです。
var primes = sieve(integers(2)); enumerate(primes, 10); // -> [2,3,5,7,11,13,17,19,23,29,31,function () { return filter(pred, tail(pipe));}]
JavaScript による pipe とエラトステネスの篩のソースは以下のもの。JavaScript っぽさとかないかもしれませんし、突っ込みどころも多々あるでしょうが多めに見てください。。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function car (ls){ | |
return ls[0]; | |
} | |
function cdr (ls){ | |
return ls[1]; | |
} | |
function functionp (v){ | |
return typeof(v) === 'function'; | |
} | |
function nullp(a) { | |
return null === a || typeof(a) === 'undefined'; | |
} | |
function makePipe (head, tail){ | |
return [head, tail]; | |
} | |
function pipeEmptyp (pipe){ | |
return nullp(pipe) || pipe.length === 0; | |
} | |
function head (pipe){ | |
return car(pipe); | |
} | |
function tail (pipe){ | |
var kdr = cdr(pipe); | |
return functionp(kdr) ? pipe[1] = kdr(): kdr; | |
} | |
function pipeElt(pipe, i){ | |
return i === 0 ? head(pipe) : pipeElt(tail(pipe), i - 1); | |
} | |
function integers (){ | |
var start = arguments[0] | 0; | |
var end = arguments[1]; | |
return (nullp(end) || start <= end) | |
? makePipe(start, function (){ return integers(start + 1, end); }) | |
: []; | |
} | |
function enumerate (pipe){ | |
var count = arguments[1]; | |
var result = nullp(arguments[2]) ? pipe : arguments[2]; | |
var ncount = count - 1; | |
return (pipeEmptyp(pipe) || count === 0) | |
? result | |
: enumerate(tail(pipe), ncount, result); | |
} | |
function filter (pred, pipe){ | |
return pred(head(pipe)) | |
? makePipe(head(pipe), function (){ return filter(pred, tail(pipe)); }) | |
: filter(pred, tail(pipe)); | |
} | |
function sieve (pipe){ | |
return makePipe(head(pipe), function (){ return filter(function (x){ | |
return 0 !== x % head(pipe); }, sieve(tail(pipe))); }); | |
} | |
var primes = sieve(integers(2)); | |
enumerate(primes, 10); | |
// -> [2,3,5,7,11,13,17,19,23,29,31,function () { return filter(pred, tail(pipe));}] |
階乗のリストだとこうでしょうか。
function facts (pipe) { var rec = function (p, acc){ var h = head(p); var cur = 0 === h ? 1 : h * acc; return makePipe(cur, function () { return rec(tail(p), cur); }); }; return rec(pipe, 1); } enumerate(facts(integers(0)), 5); // -> [1,1,2,6,24,120,function () { return rec(tail(p), cur); }] enumerate(facts(integers(0)), 10); // [1,1,2,6,24,120,720,5040,40320,362880,3628800,function () { return rec(tail(p), cur); }
ところで
JavaScript の生みの親であるブレンダン・アイクが、実はブラウザで動く Scheme が作れると聞いて!ということでネスケに入ったなんて話もあるらしいですね。
伝統的なマクロじゃないにしろ、それっぽいものがあったら JavaScript ももっと強力だったでしょうに。
そういえば
Rhino だと Scheme 由来の継続(continuation)や let が使えるそうで。Scheme 以外で明示的に継続に触れたことが無いので、試して見ます。
(ruby にも call/cc があるんでしたっけ)
例えば、何の変哲もない、というかただ再帰するだけの deep 関数を例に取ると、
function deep (n){ var ret; if (n === 0){ ret = n; } else { ret = deep(n - 1); } print(n); return ret; } deep(10); /* 0 1 2 3 4 5 6 7 8 9 10 0 */
再帰の基底条件に達した後は、今まで潜った再帰を戻るわけですよね。そのときに n が print されている状態です。
で、継続のお決まりの例である脱出をしてみたいので、以下のように書き換えて見ます。
ポイントは、実際の再帰を内部で定義した rec 関数に任せている点と、deep の先頭で継続オブジェクトを取得している点。
function deep (n){ var cont = new Continuation(); var rec = function (n){ var ret; if (n === 0){ ret = n; cont(ret); } else { ret = rec(n - 1); } print(n); return ret; }; return rec(n); } deep(10); // -> 0print されませんね。結果である 0 だけが表示されています。この結果が意図通りのものです。
基底条件に達した時点で保存しておいた継続に結果を渡して呼び出しているので、潜った再帰はなかったことに。なかったことに、と言うと語弊があるでしょうか。まぁ、気になったら Scheme でもやってみてください。
一応確認するために、rec の下で print すると、ちゃんと再帰していることがわかります。基底条件、つまり最深部まで再帰を潜って cont に渡された値が cont が宣言された以降の処理の結果となるわけです。何を言ってるかわから(ry。
function deep (n){ var cont = new Continuation(); var rec = function (n){ print(n); var ret; if (n === 0){ ret = n; cont(ret); } else { ret = rec(n - 1); } print(n); return ret; }; return rec(n); } deep(10); /* 10 9 8 7 6 5 4 3 2 1 0 0 */
追記
あ、今回使った JavaScript の処理系はRhino 1.7 release 2 2009 03 22
です。
Emacs + Rhino + js2.el + js-comint.el 最強です。
追記2
いつも教えて頂いてます!ありがとうございます!@valvallow ジェネレータが実装されてない処理系も多いみたいですね。 とりあえず、無限リストとかの考え方は無視して典型的な JavaScript らしい書き方で書いてみました。 http://gist.github.com/613523
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Primes() { | |
this.index=3; | |
this.nums=[2]; | |
} | |
Primes.prototype.next=function() { | |
var i; | |
for(i=this.index; !(this.primep(i)); i++); | |
this.index=i+1; | |
this.nums.push(i); | |
return this.nums[this.nums.length-2]; | |
}; | |
Primes.prototype.primep=function(x) { | |
var limit=Math.floor(Math.sqrt(x)); | |
var i; | |
for(i=0; this.nums[i]<=limit && x%this.nums[i]; i++){}; | |
return this.nums[i]>limit; | |
}; | |
function enumerate(ps, n) { | |
var r=[]; | |
for(var i=0; i<n; i++) r.push(ps.next()); | |
return r; | |
} | |
var primes = new Primes(); | |
print(enumerate(primes, 10)); |
0 件のコメント:
コメントを投稿