You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
14 KiB
484 lines
14 KiB
<html> |
|
<head> |
|
<title>Clocking recursion combinators</title> |
|
<style type="text/css"> |
|
@import "../../../dojo/resources/dojo.css"; |
|
</style> |
|
<script type="text/javascript" src="../../../dojo/dojo.js" data-dojo-config="isDebug:true"></script> |
|
<script type="text/javascript" src="../functional.js"></script> |
|
<script type="text/javascript" src="../functional/linrec.js"></script> |
|
<script type="text/javascript" src="../functional/tailrec.js"></script> |
|
<script type="text/javascript" src="../functional/numrec.js"></script> |
|
<script type="text/javascript" src="../functional/binrec.js"></script> |
|
<script type="text/javascript" src="../functional/multirec.js"></script> |
|
<script type="text/javascript" src="../functional/sequence.js"></script> |
|
<script type="text/javascript"> |
|
// reference implementations |
|
|
|
var linrec1 = function(cond, then, before, after){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before), |
|
after = df.lambda(after); |
|
return function(){ |
|
if(cond.apply(this, arguments)){ |
|
return then.apply(this, arguments); |
|
} |
|
var args = before.apply(this, arguments); |
|
var ret = arguments.callee.apply(this, args); |
|
return after.call(this, ret, arguments); |
|
}; |
|
}; |
|
|
|
var linrec2 = function(cond, then, before, after){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before), |
|
after = df.lambda(after); |
|
return function(){ |
|
var args = arguments, top, ret; |
|
// 1st part |
|
for(; !cond.apply(this, args); args = before.apply(this, args)){ |
|
top = {prev: top, args: args}; |
|
} |
|
ret = then.apply(this, args); |
|
//2nd part |
|
for(; top; top = top.prev){ |
|
ret = after.call(this, ret, top.args); |
|
} |
|
return ret; |
|
}; |
|
}; |
|
|
|
var tailrec1 = function(cond, then, before){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before); |
|
return function(){ |
|
if(cond.apply(this, arguments)){ |
|
return then.apply(this, arguments); |
|
} |
|
var args = before.apply(this, arguments); |
|
return arguments.callee.apply(this, args); |
|
}; |
|
}; |
|
|
|
var tailrec2 = function(cond, then, before){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before); |
|
return function(){ |
|
var args = arguments; |
|
for(; !cond.apply(this, args); args = before.apply(this, args)); |
|
return then.apply(this, args); |
|
}; |
|
}; |
|
|
|
var numrec1 = function(then, after){ |
|
var after = df.lambda(after); |
|
return function(x){ |
|
return x ? after.call(this, arguments.callee.call(this, x - 1), x) : then; |
|
}; |
|
}; |
|
|
|
var numrec2 = function(then, after){ |
|
var after = df.lambda(after); |
|
return function(x){ |
|
var ret = then, i; |
|
for(i = 1; i <= x; ++i){ |
|
ret = after.call(this, ret, i); |
|
} |
|
return ret; |
|
}; |
|
}; |
|
|
|
var binrec1 = function(cond, then, before, after){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before), |
|
after = df.lambda(after); |
|
return function(){ |
|
if(cond.apply(this, arguments)){ |
|
return then.apply(this, arguments); |
|
} |
|
var args = before.apply(this, arguments); |
|
var ret1 = arguments.callee.apply(this, args[0]); |
|
var ret2 = arguments.callee.apply(this, args[1]); |
|
return after.call(this, ret1, ret2, arguments); |
|
}; |
|
}; |
|
|
|
var binrec2 = function(cond, then, before, after){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before), |
|
after = df.lambda(after); |
|
return function(){ |
|
var top1, top2, ret, args = arguments; |
|
// first part: start the pump |
|
while(!cond.apply(this, args)){ |
|
ret = before.apply(this, args); |
|
top1 = {prev: top1, args: ret[1]}; |
|
top2 = {prev: top2, args: args}; |
|
args = ret[0]; |
|
} |
|
for(;;){ |
|
// second part: mop up |
|
do{ |
|
ret = then.apply(this, args); |
|
if(!top2){ |
|
return ret; |
|
} |
|
while("ret" in top2){ |
|
ret = after.call(this, top2.ret, ret, top2.args); |
|
if(!(top2 = top2.prev)){ |
|
return ret; |
|
} |
|
} |
|
top2.ret = ret; |
|
args = top1.args; |
|
top1 = top1.prev; |
|
}while(cond.apply(this, args)); |
|
// first part (encore) |
|
do{ |
|
ret = before.apply(this, args); |
|
top1 = {prev: top1, args: ret[1]}; |
|
top2 = {prev: top2, args: args}; |
|
args = ret[0]; |
|
}while(!cond.apply(this, args)); |
|
} |
|
}; |
|
}; |
|
|
|
var multirec1 = function(cond, then, before, after){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before), |
|
after = df.lambda(after); |
|
return function(){ |
|
if(cond.apply(this, arguments)){ |
|
return then.apply(this, arguments); |
|
} |
|
var args = before.apply(this, arguments), |
|
ret = new Array(args.length); |
|
for(var i = 0; i < args.length; ++i){ |
|
ret[i] = arguments.callee.apply(this, args[i]); |
|
} |
|
return after.call(this, ret, arguments); |
|
}; |
|
}; |
|
|
|
var multirec2 = function(cond, then, before, after){ |
|
var cond = df.lambda(cond), |
|
then = df.lambda(then), |
|
before = df.lambda(before), |
|
after = df.lambda(after); |
|
return function(){ |
|
var top = {args: arguments}, args, ret, parent, i; |
|
for(;;){ |
|
for(;;){ |
|
if(top.old){ |
|
ret = after.call(this, top.ret, top.old); |
|
break; |
|
} |
|
args = top.args; |
|
if(cond.apply(this, args)){ |
|
ret = then.apply(this, args); |
|
break; |
|
} |
|
top.old = args; |
|
args = before.apply(this, args); |
|
top.ret = []; |
|
parent = top; |
|
for(i = args.length - 1; i >= 0; --i){ |
|
top = {prev: top, args: args[i], parent: parent}; |
|
} |
|
} |
|
if(!(parent = top.parent)){ |
|
return ret; |
|
} |
|
parent.ret.push(ret); |
|
top = top.prev; |
|
} |
|
}; |
|
}; |
|
|
|
// tests |
|
|
|
var clock = function(body){ |
|
var b = new Date(); |
|
body(); |
|
var e = new Date(); |
|
return e.getTime() - b.getTime(); // in ms |
|
}; |
|
|
|
var log = function(name, body){ |
|
var ms = clock(body); |
|
console.log(name + ":", ms); |
|
}; |
|
|
|
var LEN1 = 15, LEN2 = 10, ITER = 1000, tests = {}, |
|
df = dojox.lang.functional, |
|
sample1 = df.repeat(LEN1, "+1", 0), |
|
sample2 = df.repeat(LEN2, "+1", 0); |
|
|
|
var fact1 = function(n){ |
|
var ret = 1; |
|
for(var i = 2; i <= n; ++i){ |
|
ret *= i; |
|
} |
|
return ret; |
|
}; |
|
tests["factorial: raw iterative"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact1); |
|
} |
|
}; |
|
|
|
var fact2 = function(n){ return n ? n * arguments.callee.call(this, n - 1) : 1; }; |
|
tests["factorial: raw recursion"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact2); |
|
} |
|
}; |
|
|
|
var fact3_1 = linrec1("<= 1", "1", "[n - 1]", "a * b[0]"); |
|
tests["factorial: linrec1 (recursive reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact3_1); |
|
} |
|
}; |
|
|
|
var fact3_2 = linrec2("<= 1", "1", "[n - 1]", "a * b[0]"); |
|
tests["factorial: linrec2 (iterative reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact3_2); |
|
} |
|
}; |
|
|
|
var fact3 = df.linrec("<= 1", "1", "[n - 1]", "a * b[0]"); |
|
tests["factorial: linrec"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact3); |
|
} |
|
}; |
|
|
|
var fact4_1 = numrec1(1, "*"); |
|
tests["factorial: numrec1 (recursive reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact4_1); |
|
} |
|
}; |
|
|
|
var fact4_2 = numrec2(1, "*"); |
|
tests["factorial: numrec2 (iterative reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact4_2); |
|
} |
|
}; |
|
|
|
var fact4 = df.numrec(1, "*"); |
|
tests["factorial: numrec"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact4); |
|
} |
|
}; |
|
|
|
var fact5_1a = tailrec1("<= 1", "a, b -> b", "[n - 1, n * acc]"); |
|
var fact5_1 = function(n){ return fact5_1a(n, 1); }; |
|
tests["factorial: tailrec1 (recursive reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact5_1); |
|
} |
|
}; |
|
|
|
var fact5_2a = tailrec2("<= 1", "a, b -> b", "[n - 1, n * acc]"); |
|
var fact5_2 = function(n){ return fact5_2a(n, 1); }; |
|
tests["factorial: tailrec2 (iterative reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact5_2); |
|
} |
|
}; |
|
|
|
var fact5a = df.tailrec("<= 1", "a, b -> b", "[n - 1, n * acc]"); |
|
var fact5 = function(n){ return fact5a(n, 1); }; |
|
tests["factorial: tailrec"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact5); |
|
} |
|
}; |
|
|
|
var fact6_1 = multirec1("<= 0", "1", "[[n - 1]]", "a[0] * b[0]"); |
|
tests["factorial: multirec1 (recursive reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact6_1); |
|
} |
|
}; |
|
|
|
var fact6_2 = multirec2("<= 0", "1", "[[n - 1]]", "a[0] * b[0]"); |
|
tests["factorial: multirec2 (iterative reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact6_2); |
|
} |
|
}; |
|
|
|
var fact6 = df.multirec("<= 0", "1", "[[n - 1]]", "a[0] * b[0]"); |
|
tests["factorial: multirec"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample1, fact6); |
|
} |
|
}; |
|
|
|
var fib1 = function(n){ |
|
var a = 1, b = 1; |
|
for(var i = 2; i <= n; ++i){ |
|
var c = a + b; |
|
b = a; |
|
a = c; |
|
} |
|
return a; |
|
}; |
|
tests["fibonacci: raw iterative"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib1); |
|
} |
|
}; |
|
|
|
var fib2 = function(n){ return n <= 1 ? 1 : arguments.callee.call(this, n - 1) + arguments.callee.call(this, n - 2); }; |
|
tests["fibonacci: raw recursion"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib2); |
|
} |
|
}; |
|
|
|
var fib3a = function(n, next, result){ return n <= 0 ? result : arguments.callee.call(this, n - 1, next + result, next); }; |
|
var fib3 = function(n){ return fib3a(n, 1, 1); }; |
|
tests["fibonacci: raw tail recursion"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib3); |
|
} |
|
}; |
|
|
|
var fib4_1 = binrec1("<= 1", "1", "[[n - 1], [n - 2]]", "+"); |
|
tests["fibonacci: binrec1 (recursive reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib4_1); |
|
} |
|
}; |
|
|
|
var fib4_2 = binrec2("<= 1", "1", "[[n - 1], [n - 2]]", "+"); |
|
tests["fibonacci: binrec2 (iterative reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib4_2); |
|
} |
|
}; |
|
|
|
var fib4 = df.binrec("<= 1", "1", "[[n - 1], [n - 2]]", "+"); |
|
tests["fibonacci: binrec"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib4); |
|
} |
|
}; |
|
|
|
var fib5_1a = tailrec1("<= 0", "a, b, c -> c", "[n - 1, next + result, next]"); |
|
var fib5_1 = function(n){ return fib5_1a(n, 1, 1); }; |
|
tests["fibonacci: tailrec1 (recursive reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib5_1); |
|
} |
|
}; |
|
|
|
var fib5_2a = tailrec2("<= 0", "a, b, c -> c", "[n - 1, next + result, next]"); |
|
var fib5_2 = function(n){ return fib5_2a(n, 1, 1); }; |
|
tests["fibonacci: tailrec2 (iterative reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib5_2); |
|
} |
|
}; |
|
|
|
var fib5a = df.tailrec("<= 0", "a, b, c -> c", "[n - 1, next + result, next]"); |
|
var fib5 = function(n){ return fib5a(n, 1, 1); }; |
|
tests["fibonacci: tailrec"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib5); |
|
} |
|
}; |
|
|
|
var fib6_1 = multirec1("<= 1", "1", "[[n - 1], [n - 2]]", "a[0] + a[1]"); |
|
tests["fibonacci: multirec1 (recursive reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib6_1); |
|
} |
|
}; |
|
|
|
var fib6_2 = multirec2("<= 1", "1", "[[n - 1], [n - 2]]", "a[0] + a[1]"); |
|
tests["fibonacci: multirec2 (iterative reference)"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib6_2); |
|
} |
|
}; |
|
|
|
var fib6 = df.multirec("<= 1", "1", "[[n - 1], [n - 2]]", "a[0] + a[1]"); |
|
tests["fibonacci: multirec"] = function(){ |
|
for(var i = 0; i < ITER; ++i){ |
|
df.forEach(sample2, fib6); |
|
} |
|
}; |
|
|
|
var keys = df.keys(tests), i = 0; |
|
|
|
var doTest = function(){ |
|
log(keys[i], tests[keys[i]]); |
|
++i; |
|
if(i < keys.length){ |
|
setTimeout(doTest, 20); |
|
}else{ |
|
console.log("that's all"); |
|
} |
|
}; |
|
|
|
var test = function(){ |
|
i = 0; |
|
setTimeout(doTest, 20); |
|
}; |
|
|
|
dojo.addOnLoad(function(){ |
|
// sanity check |
|
|
|
console.assert(fact1(5) == 120, "fact1"); |
|
console.assert(fact2(5) == 120, "fact2"); |
|
console.assert(fact3_1(5) == 120, "fact3_1"); |
|
console.assert(fact3_2(5) == 120, "fact3_2"); |
|
console.assert(fact3(5) == 120, "fact3"); |
|
console.assert(fact4_1(5) == 120, "fact4_1"); |
|
console.assert(fact4_2(5) == 120, "fact4_2"); |
|
console.assert(fact4(5) == 120, "fact4"); |
|
console.assert(fact5_1(5) == 120, "fact5_1"); |
|
console.assert(fact5_2(5) == 120, "fact5_2"); |
|
console.assert(fact5(5) == 120, "fact5"); |
|
console.assert(fact6_1(5) == 120, "fact6_1"); |
|
console.assert(fact6_2(5) == 120, "fact6_2"); |
|
console.assert(fact6(5) == 120, "fact6"); |
|
|
|
console.assert(fib1(5) == 8, "fib1"); |
|
console.assert(fib2(5) == 8, "fib2"); |
|
console.assert(fib3(5) == 8, "fib3"); |
|
console.assert(fib4_1(5) == 8, "fib4_1"); |
|
console.assert(fib4_2(5) == 8, "fib4_2"); |
|
console.assert(fib4(5) == 8, "fib4"); |
|
console.assert(fib5_1(5) == 8, "fib5_1"); |
|
console.assert(fib5_2(5) == 8, "fib5_2"); |
|
console.assert(fib5(5) == 8, "fib5"); |
|
console.assert(fib6_1(5) == 8, "fib6_1"); |
|
console.assert(fib6_2(5) == 8, "fib6_2"); |
|
console.assert(fib6(5) == 8, "fib6"); |
|
|
|
console.log("sanity check finished"); |
|
}); |
|
</script> |
|
</head> |
|
<body> |
|
<p>This test is meant to run with Firebug. Open the console to see the output.</p> |
|
<p><button onclick="test()">Start</button></p> |
|
</body> |
|
</html>
|
|
|