diff options
Diffstat (limited to 'js/dojo/dojox/lang/functional')
| -rw-r--r-- | js/dojo/dojox/lang/functional/array.js | 168 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/binrec.js | 177 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/curry.js | 98 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/fold.js | 122 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/lambda.js | 133 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/linrec.js | 147 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/listcomp.js | 55 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/multirec.js | 169 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/numrec.js | 95 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/object.js | 77 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/reversed.js | 76 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/scan.js | 104 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/sequence.js | 39 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/tailrec.js | 120 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/util.js | 51 | ||||
| -rw-r--r-- | js/dojo/dojox/lang/functional/zip.js | 45 |
16 files changed, 1676 insertions, 0 deletions
diff --git a/js/dojo/dojox/lang/functional/array.js b/js/dojo/dojox/lang/functional/array.js new file mode 100644 index 0000000..8c47b5c --- /dev/null +++ b/js/dojo/dojox/lang/functional/array.js @@ -0,0 +1,168 @@ +//>>built +define("dojox/lang/functional/array", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "./lambda"], + function(dojo, lang, arr, win, df){ + +// This module adds high-level functions and related constructs: +// - array-processing functions similar to standard JS functions + +// Notes: +// - this module provides JS standard methods similar to high-level functions in dojo/_base/array.js: +// forEach, map, filter, every, some + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // JS 1.6 standard array functions, which can take a lambda as a parameter. + // Consider using dojo._base.array functions, if you don't need the lambda support. + filter: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with all elements that pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t = [], v, i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + v = a.next(); + if(f.call(o, v, i++, a)){ t.push(v); } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + } + } + return t; // Array + }, + forEach: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: executes a provided function once per array element. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; f.call(o, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext(); f.call(o, a.next(), i++, a)); + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + f.call(o, a[i], i, a); + } + } + } + return o; // Object + }, + map: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with the results of calling + // a provided function on every element in this array. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t, n, i; + if(lang.isArray(a)){ + // array + t = new Array(n = a.length); + for(i = 0; i < n; t[i] = f.call(o, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + t = []; + for(i = 0; a.hasNext(); t.push(f.call(o, a.next(), i++, a))); + }else{ + // object/dictionary + t = []; + for(i in a){ + if(!(i in empty)){ + t.push(f.call(o, a[i], i, a)); + } + } + } + return t; // Array + }, + every: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether all elements in the array pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + if(!f.call(o, a.next(), i++, a)){ + return false; // Boolean + } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + } + } + return true; // Boolean + }, + some: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether some element in the array passes the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + if(f.call(o, a.next(), i++, a)){ + return true; // Boolean + } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + } + } + return false; // Boolean + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/binrec.js b/js/dojo/dojox/lang/functional/binrec.js new file mode 100644 index 0000000..f32ec8e --- /dev/null +++ b/js/dojo/dojox/lang/functional/binrec.js @@ -0,0 +1,177 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/binrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.binrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a binary recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _x ="_x", _z_r_r_z_a = ["_z.r", "_r", "_z.a"]; + + df.binrec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before, + /*Function|String|Array*/ after){ + // summary: + // Generates a function for the binary recursion pattern. + // All parameter functions are called in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself. + // The returned value should be an array of two variable, which are used to call + // the generated function recursively twice in row starting from the first item. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts three parameters: two returned values from recursive steps, and + // the original array of parameters used with all other functions. + // The returned value will be returned as the value of the generated function. + + var c, t, b, a, cs, ts, bs, as, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + if(typeof after == "string"){ + as = inline(after, _z_r_r_z_a, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _z.r, _r, _z.a)"; + dict2["_a=_t.a"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _x=arguments,_y,_z,_r".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=_x.callee," + locals2.join(",") : "", + t ? (locals2.length ? ",_t=_t.t" : "_t=_x.callee.t") : "", + ";while(!", + cs, + "){_r=", + bs, + ";_y={p:_y,a:_r[1]};_z={p:_z,a:_x};_x=_r[0]}for(;;){do{_r=", + ts, + ";if(!_z)return _r;while(\"r\" in _z){_r=", + as, + ";if(!(_z=_z.p))return _r}_z.r=_r;_x=_y.a;_y=_y.p}while(", + cs, + ");do{_r=", + bs, + ";_y={p:_y,a:_r[1]};_z={p:_z,a:_x};_x=_r[0]}while(!", + cs, + ")}" + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +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); + }; +}; + +2) The original iterative version (before minification and inlining): + +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)); + } + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/curry.js b/js/dojo/dojox/lang/functional/curry.js new file mode 100644 index 0000000..6f79f01 --- /dev/null +++ b/js/dojo/dojox/lang/functional/curry.js @@ -0,0 +1,98 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/curry", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.curry"); + +dojo.require("dojox.lang.functional.lambda"); + +// This module adds high-level functions and related constructs: +// - currying and partial functions +// - argument pre-processing: mixer and flip + +// Acknoledgements: +// - partial() is based on work by Oliver Steele +// (http://osteele.com/sources/javascript/functional/functional.js) +// which was published under MIT License + +// Defined methods: +// - take any valid lambda argument as the functional argument + +(function(){ + var df = dojox.lang.functional, ap = Array.prototype; + + var currying = function(/*Object*/ info){ + return function(){ // Function + var args = info.args.concat(ap.slice.call(arguments, 0)); + if(arguments.length + info.args.length < info.arity){ + return currying({func: info.func, arity: info.arity, args: args}); + } + return info.func.apply(this, args); + }; + }; + + dojo.mixin(df, { + // currying and partial functions + curry: function(/*Function|String|Array*/ f, /*Number?*/ arity){ + // summary: curries a function until the arity is satisfied, at + // which point it returns the calculated value. + f = df.lambda(f); + arity = typeof arity == "number" ? arity : f.length; + return currying({func: f, arity: arity, args: []}); // Function + }, + arg: {}, // marker for missing arguments + partial: function(/*Function|String|Array*/ f){ + // summary: creates a function where some arguments are bound, and + // some arguments (marked as dojox.lang.functional.arg) are will be + // accepted by the final function in the order they are encountered. + // description: This method is used to produce partially bound + // functions. If you want to change the order of arguments, use + // dojox.lang.functional.mixer() or dojox.lang.functional.flip(). + var a = arguments, l = a.length, args = new Array(l - 1), p = [], i = 1, t; + f = df.lambda(f); + for(; i < l; ++i){ + t = a[i]; + args[i - 1] = t; + if(t === df.arg){ + p.push(i - 1); + } + } + return function(){ // Function + var t = ap.slice.call(args, 0), // clone the array + i = 0, l = p.length; + for(; i < l; ++i){ + t[p[i]] = arguments[i]; + } + return f.apply(this, t); + }; + }, + // argument pre-processing + mixer: function(/*Function|String|Array*/ f, /*Array*/ mix){ + // summary: changes the order of arguments using an array of + // numbers mix --- i-th argument comes from mix[i]-th place + // of supplied arguments. + f = df.lambda(f); + return function(){ // Function + var t = new Array(mix.length), i = 0, l = mix.length; + for(; i < l; ++i){ + t[i] = arguments[mix[i]]; + } + return f.apply(this, t); + }; + }, + flip: function(/*Function|String|Array*/ f){ + // summary: changes the order of arguments by reversing their + // order. + f = df.lambda(f); + return function(){ // Function + // reverse arguments + var a = arguments, l = a.length - 1, t = new Array(l + 1), i = 0; + for(; i <= l; ++i){ + t[l - i] = a[i]; + } + return f.apply(this, t); + }; + } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/functional/fold.js b/js/dojo/dojox/lang/functional/fold.js new file mode 100644 index 0000000..43a8922 --- /dev/null +++ b/js/dojo/dojox/lang/functional/fold.js @@ -0,0 +1,122 @@ +//>>built +define("dojox/lang/functional/fold", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "./lambda"], + function(lang, arr, win, df){ + +// This module adds high-level functions and related constructs: +// - "fold" family of functions + +// Notes: +// - missing high-level functions are provided with the compatible API: +// foldl, foldl1, foldr, foldr1 +// - missing JS standard functions are provided with the compatible API: +// reduce, reduceRight +// - the fold's counterpart: unfold + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument (only foldl, foldl1, and reduce) + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // classic reduce-class functions + foldl: function(/*Array|String|Object*/ a, /*Function*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right using a seed value as a starting point; returns the final + // value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext(); z = f.call(o, z, a.next(), i++, a)); + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + z = f.call(o, z, a[i], i, a); + } + } + } + return z; // Object + }, + foldl1: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right; returns the final value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var z, i, n; + if(lang.isArray(a)){ + // array + z = a[0]; + for(i = 1, n = a.length; i < n; z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + if(a.hasNext()){ + z = a.next(); + for(i = 1; a.hasNext(); z = f.call(o, z, a.next(), i++, a)); + } + }else{ + // object/dictionary + var first = true; + for(i in a){ + if(!(i in empty)){ + if(first){ + z = a[i]; + first = false; + }else{ + z = f.call(o, z, a[i], i, a); + } + } + } + } + return z; // Object + }, + foldr: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left using a seed value as a starting point; returns the final + // value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length; i > 0; --i, z = f.call(o, z, a[i], i, a)); + return z; // Object + }, + foldr1: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left; returns the final value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var n = a.length, z = a[n - 1], i = n - 1; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a)); + return z; // Object + }, + // JS 1.8 standard array functions, which can take a lambda as a parameter. + reduce: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ z){ + // summary: apply a function simultaneously against two values of the array + // (from left-to-right) as to reduce it to a single value. + return arguments.length < 3 ? df.foldl1(a, f) : df.foldl(a, f, z); // Object + }, + reduceRight: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ z){ + // summary: apply a function simultaneously against two values of the array + // (from right-to-left) as to reduce it to a single value. + return arguments.length < 3 ? df.foldr1(a, f) : df.foldr(a, f, z); // Object + }, + // the fold's counterpart: unfold + unfold: function(/*Function|String|Array*/ pr, /*Function|String|Array*/ f, + /*Function|String|Array*/ g, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by unfolding a value + o = o || win.global; f = df.lambda(f); g = df.lambda(g); pr = df.lambda(pr); + var t = []; + for(; !pr.call(o, z); t.push(f.call(o, z)), z = g.call(o, z)); + return t; // Array + } + }); +}); diff --git a/js/dojo/dojox/lang/functional/lambda.js b/js/dojo/dojox/lang/functional/lambda.js new file mode 100644 index 0000000..b5a9f20 --- /dev/null +++ b/js/dojo/dojox/lang/functional/lambda.js @@ -0,0 +1,133 @@ +//>>built +define("dojox/lang/functional/lambda", ["../..", "dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array"], function(dojox, dojo, lang, arr){ + var df = lang.getObject("lang.functional", true, dojox); + +// This module adds high-level functions and related constructs: +// - anonymous functions built from the string + +// Acknoledgements: +// - lambda() is based on work by Oliver Steele +// (http://osteele.com/sources/javascript/functional/functional.js) +// which was published under MIT License + +// Notes: +// - lambda() produces functions, which after the compilation step are +// as fast as regular JS functions (at least theoretically). + +// Lambda input values: +// - returns functions unchanged +// - converts strings to functions +// - converts arrays to a functional composition + + var lcache = {}; + + // split() is augmented on IE6 to ensure the uniform behavior + var split = "ab".split(/a*/).length > 1 ? String.prototype.split : + function(sep){ + var r = this.split.call(this, sep), + m = sep.exec(this); + if(m && m.index == 0){ r.unshift(""); } + return r; + }; + + var lambda = function(/*String*/ s){ + var args = [], sects = split.call(s, /\s*->\s*/m); + if(sects.length > 1){ + while(sects.length){ + s = sects.pop(); + args = sects.pop().split(/\s*,\s*|\s+/m); + if(sects.length){ sects.push("(function(" + args + "){return (" + s + ")})"); } + } + }else if(s.match(/\b_\b/)){ + args = ["_"]; + }else{ + var l = s.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m), + r = s.match(/[+\-*\/%&|\^\.=<>!]\s*$/m); + if(l || r){ + if(l){ + args.push("$1"); + s = "$1" + s; + } + if(r){ + args.push("$2"); + s = s + "$2"; + } + }else{ + // the point of the long regex below is to exclude all well-known + // lower-case words from the list of potential arguments + var vars = s. + replace(/(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*:|this|true|false|null|undefined|typeof|instanceof|in|delete|new|void|arguments|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|eval|isFinite|isNaN|parseFloat|parseInt|unescape|dojo|dijit|dojox|window|document|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, ""). + match(/([a-z_$][a-z_$\d]*)/gi) || [], t = {}; + arr.forEach(vars, function(v){ + if(!(v in t)){ + args.push(v); + t[v] = 1; + } + }); + } + } + return {args: args, body: s}; // Object + }; + + var compose = function(/*Array*/ a){ + return a.length ? + function(){ + var i = a.length - 1, x = df.lambda(a[i]).apply(this, arguments); + for(--i; i >= 0; --i){ x = df.lambda(a[i]).call(this, x); } + return x; + } + : + // identity + function(x){ return x; }; + }; + + lang.mixin(df, { + // lambda + rawLambda: function(/*String*/ s){ + // summary: + // builds a function from a snippet, or array (composing), + // returns an object describing the function; functions are + // passed through unmodified. + // description: + // This method is to normalize a functional representation (a + // text snippet) to an object that contains an array of + // arguments, and a body , which is used to calculate the + // returning value. + return lambda(s); // Object + }, + buildLambda: function(/*String*/ s){ + // summary: + // builds a function from a snippet, returns a string, which + // represents the function. + // description: + // This method returns a textual representation of a function + // built from the snippet. It is meant to be evaled in the + // proper context, so local variables can be pulled from the + // environment. + s = lambda(s); + return "function(" + s.args.join(",") + "){return (" + s.body + ");}"; // String + }, + lambda: function(/*Function|String|Array*/ s){ + // summary: + // builds a function from a snippet, or array (composing), + // returns a function object; functions are passed through + // unmodified. + // description: + // This method is used to normalize a functional + // representation (a text snippet, an array, or a function) to + // a function object. + if(typeof s == "function"){ return s; } + if(s instanceof Array){ return compose(s); } + if(s in lcache){ return lcache[s]; } + s = lambda(s); + return lcache[s] = new Function(s.args, "return (" + s.body + ");"); // Function + }, + clearLambdaCache: function(){ + // summary: + // clears internal cache of lambdas + lcache = {}; + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/linrec.js b/js/dojo/dojox/lang/functional/linrec.js new file mode 100644 index 0000000..066ff54 --- /dev/null +++ b/js/dojo/dojox/lang/functional/linrec.js @@ -0,0 +1,147 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/linrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.linrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a linear recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _x ="_x", _r_y_a = ["_r", "_y.a"]; + + df.linrec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before, + /*Function|String|Array*/ after){ + // summary: + // Generates a function for the linear recursion pattern. + // All parameter functions are called in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself. + // The returned value should be an array, which is used to call + // the generated function recursively. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts two parameters: the returned value from the recursive step, and + // the original array of parameters used with all other functions. + // The returned value will be returned as the value of the generated function. + + var c, t, b, a, cs, ts, bs, as, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + if(typeof after == "string"){ + as = inline(after, _r_y_a, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _r, _y.a)"; + dict2["_a=_t.a"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _x=arguments,_y,_r".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=_x.callee," + locals2.join(",") : t ? ",_t=_x.callee" : "", + ";for(;!", + cs, + ";_x=", + bs, + "){_y={p:_y,a:_x}}_r=", + ts, + ";for(;_y;_y=_y.p){_r=", + as, + "}return _r" + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +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); + }; +}; + +2) The original iterative version (before minification and inlining): + +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; + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/listcomp.js b/js/dojo/dojox/lang/functional/listcomp.js new file mode 100644 index 0000000..e73384e --- /dev/null +++ b/js/dojo/dojox/lang/functional/listcomp.js @@ -0,0 +1,55 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/listcomp", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.listcomp"); + +// This module adds high-level functions and related constructs: +// - list comprehensions similar to JavaScript 1.7 + +// Notes: +// - listcomp() produces functions, which after the compilation step are +// as fast as regular JS functions (at least theoretically). + +(function(){ + var g_re = /\bfor\b|\bif\b/gm; + + var listcomp = function(/*String*/ s){ + var frag = s.split(g_re), act = s.match(g_re), + head = ["var r = [];"], tail = [], i = 0, l = act.length; + while(i < l){ + var a = act[i], f = frag[++i]; + if(a == "for" && !/^\s*\(\s*(;|var)/.test(f)){ + f = f.replace(/^\s*\(/, "(var "); + } + head.push(a, f, "{"); + tail.push("}"); + } + return head.join("") + "r.push(" + frag[0] + ");" + tail.join("") + "return r;"; // String + }; + + dojo.mixin(dojox.lang.functional, { + buildListcomp: function(/*String*/ s){ + // summary: builds a function from a text snippet, which represents a valid + // JS 1.7 list comprehension, returns a string, which represents the function. + // description: This method returns a textual representation of a function + // built from the list comprehension text snippet (conformant to JS 1.7). + // It is meant to be evaled in the proper context, so local variable can be + // pulled from the environment. + return "function(){" + listcomp(s) + "}"; // String + }, + compileListcomp: function(/*String*/ s){ + // summary: builds a function from a text snippet, which represents a valid + // JS 1.7 list comprehension, returns a function object. + // description: This method returns a function built from the list + // comprehension text snippet (conformant to JS 1.7). It is meant to be + // reused several times. + return new Function([], listcomp(s)); // Function + }, + listcomp: function(/*String*/ s){ + // summary: executes the list comprehension building an array. + return (new Function([], listcomp(s)))(); // Array + } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/functional/multirec.js b/js/dojo/dojox/lang/functional/multirec.js new file mode 100644 index 0000000..0f50668 --- /dev/null +++ b/js/dojo/dojox/lang/functional/multirec.js @@ -0,0 +1,169 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/multirec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.multirec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a multi-way recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _x ="_x", _y_r_y_o = ["_y.r", "_y.o"]; + + df.multirec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before, + /*Function|String|Array*/ after){ + // summary: + // Generates a function for the multi-way recursion pattern. + // All parameter functions are called in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself. + // The returned value should be an array, which is used to call + // the generated function recursively. Each member of the array should be + // an array of parameters. The length of it defines how many times + // the generated function is called recursively. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts two parameters: the array of returned values from recursive steps, + // and the original array of parameters used with all other functions. + // The returned value will be returned as the value of the generated function. + + var c, t, b, a, cs, ts, bs, as, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + if(typeof after == "string"){ + as = inline(after, _y_r_y_o, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _y.r, _y.o)"; + dict2["_a=_t.a"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _y={a:arguments},_x,_r,_z,_i".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=arguments.callee," + locals2.join(",") : "", + t ? (locals2.length ? ",_t=_t.t" : "_t=arguments.callee.t") : "", + ";for(;;){for(;;){if(_y.o){_r=", + as, + ";break}_x=_y.a;if(", + cs, + "){_r=", + ts, + ";break}_y.o=_x;_x=", + bs, + ";_y.r=[];_z=_y;for(_i=_x.length-1;_i>=0;--_i){_y={p:_y,a:_x[_i],z:_z}}}if(!(_z=_y.z)){return _r}_z.r.push(_r);_y=_y.p}" + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +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); + }; +}; + +2) The original iterative version (before minification and inlining): + +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; + } + }; +}; + +*/ + +}); diff --git a/js/dojo/dojox/lang/functional/numrec.js b/js/dojo/dojox/lang/functional/numrec.js new file mode 100644 index 0000000..b9e76c9 --- /dev/null +++ b/js/dojo/dojox/lang/functional/numrec.js @@ -0,0 +1,95 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/numrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.numrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a simplified numeric linear recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, + _r_i = ["_r", "_i"]; + + df.numrec = function(/*Object*/ then, /*Function|String|Array*/ after){ + // summary: + // Generates a function for the simplified numeric linear recursion pattern. + // All parameter functions are called in the context of "this" object. + // description: + // This is a simplification of the linear recursion combinator: + // - the generated function takes one numeric parameter "x", + // - the "cond" is fixed and checks for 0. + // - the "before" is fixed and the generated function is called with "x - 1". + // - the "above is called with two parameters: the return from the generated + // function, and with "x". + // - as you can see the recursion is done by decreasing the parameter, + // and calling itself until it reaches 0. + // then: + // The value, which is used upon termination of the recursion. + // It will be returned as the value of the generated function. + // above: + // The lambda expression, which is called after the recursive step. + // It accepts two parameters: the returned value from the recursive step, and + // the original parameter. The returned value will be returned as the value of + // the generated function. + + var a, as, dict = {}, + add2dict = function(x){ dict[x] = 1; }; + if(typeof after == "string"){ + as = inline(after, _r_i, add2dict); + }else{ + a = df.lambda(after); + as = "_a.call(this, _r, _i)"; + } + var locals = df.keys(dict), + f = new Function(["_x"], "var _t=arguments.callee,_r=_t.t,_i".concat( // Function + locals.length ? "," + locals.join(",") : "", + a ? ",_a=_t.a" : "", + ";for(_i=1;_i<=_x;++_i){_r=", + as, + "}return _r" + )); + f.t = then; + if(a){ f.a = a; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +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; + }; +}; + +2) The original iterative version (before minification and inlining): + +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; + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/object.js b/js/dojo/dojox/lang/functional/object.js new file mode 100644 index 0000000..c35ef4f --- /dev/null +++ b/js/dojo/dojox/lang/functional/object.js @@ -0,0 +1,77 @@ +//>>built +define("dojox/lang/functional/object", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/window", "./lambda"], function(dojo, lang, win, df){ + +// This module adds high-level functions and related constructs: +// - object/dictionary helpers + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - skip all attributes that are present in the empty object +// (IE and/or 3rd-party libraries). + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // object helpers + keys: function(/*Object*/ obj){ + // summary: returns an array of all keys in the object + var t = []; + for(var i in obj){ + if(!(i in empty)){ + t.push(i); + } + } + return t; // Array + }, + values: function(/*Object*/ obj){ + // summary: returns an array of all values in the object + var t = []; + for(var i in obj){ + if(!(i in empty)){ + t.push(obj[i]); + } + } + return t; // Array + }, + filterIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates new object with all attributes that pass the test + // implemented by the provided function. + o = o || win.global; f = df.lambda(f); + var t = {}, v, i; + for(i in obj){ + if(!(i in empty)){ + v = obj[i]; + if(f.call(o, v, i, obj)){ t[i] = v; } + } + } + return t; // Object + }, + forIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: iterates over all object attributes. + o = o || win.global; f = df.lambda(f); + for(var i in obj){ + if(!(i in empty)){ + f.call(o, obj[i], i, obj); + } + } + return o; // Object + }, + mapIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates new object with the results of calling + // a provided function on every attribute in this object. + o = o || win.global; f = df.lambda(f); + var t = {}, i; + for(i in obj){ + if(!(i in empty)){ + t[i] = f.call(o, obj[i], i, obj); + } + } + return t; // Object + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/reversed.js b/js/dojo/dojox/lang/functional/reversed.js new file mode 100644 index 0000000..d049be9 --- /dev/null +++ b/js/dojo/dojox/lang/functional/reversed.js @@ -0,0 +1,76 @@ +//>>built +define("dojox/lang/functional/reversed", ["dojo/_base/lang", "dojo/_base/window" ,"./lambda"], + function(lang, win, df){ +// This module adds high-level functions and related constructs: +// - reversed versions of array-processing functions similar to standard JS functions + +// Notes: +// - this module provides reversed versions of standard array-processing functions: +// forEachRev, mapRev, filterRev + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // JS 1.6 standard array functions, which can take a lambda as a parameter. + // Consider using dojo._base.array functions, if you don't need the lambda support. + filterRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with all elements that pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t = [], v, i = a.length - 1; + for(; i >= 0; --i){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + return t; // Array + }, + forEachRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: executes a provided function once per array element. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; f.call(o, a[i], i, a), --i); + }, + mapRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with the results of calling + // a provided function on every element in this array. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var n = a.length, t = new Array(n), i = n - 1, j = 0; + for(; i >= 0; t[j++] = f.call(o, a[i], i, a), --i); + return t; // Array + }, + everyRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether all elements in the array pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; --i){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + return true; // Boolean + }, + someRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether some element in the array passes the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; --i){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + return false; // Boolean + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/scan.js b/js/dojo/dojox/lang/functional/scan.js new file mode 100644 index 0000000..8abbe47 --- /dev/null +++ b/js/dojo/dojox/lang/functional/scan.js @@ -0,0 +1,104 @@ +//>>built +define("dojox/lang/functional/scan", ["dojo/_base/kernel", "dojo/_base/lang", "./lambda"], function(d, darray, df){ + +// This module adds high-level functions and related constructs: +// - "scan" family of functions + +// Notes: +// - missing high-level functions are provided with the compatible API: +// scanl, scanl1, scanr, scanr1 + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument (only scanl, and scanl1) + + var empty = {}; + + d.mixin(df, { + // classic reduce-class functions + scanl: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right using a seed value as a starting point; returns an array + // of values produced by foldl() at that point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var t, n, i; + if(d.isArray(a)){ + // array + t = new Array((n = a.length) + 1); + t[0] = z; + for(i = 0; i < n; z = f.call(o, z, a[i], i, a), t[++i] = z); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + t = [z]; + for(i = 0; a.hasNext(); t.push(z = f.call(o, z, a.next(), i++, a))); + }else{ + // object/dictionary + t = [z]; + for(i in a){ + if(!(i in empty)){ + t.push(z = f.call(o, z, a[i], i, a)); + } + } + } + return t; // Array + }, + scanl1: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right; returns an array of values produced by foldl1() at that + // point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var t, n, z, first = true; + if(d.isArray(a)){ + // array + t = new Array(n = a.length); + t[0] = z = a[0]; + for(var i = 1; i < n; t[i] = z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + if(a.hasNext()){ + t = [z = a.next()]; + for(i = 1; a.hasNext(); t.push(z = f.call(o, z, a.next(), i++, a))); + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(first){ + t = [z = a[i]]; + first = false; + }else{ + t.push(z = f.call(o, z, a[i], i, a)); + } + } + } + } + return t; // Array + }, + scanr: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left using a seed value as a starting point; returns an array + // of values produced by foldr() at that point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var n = a.length, t = new Array(n + 1), i = n; + t[n] = z; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z); + return t; // Array + }, + scanr1: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left; returns an array of values produced by foldr1() at that + // point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var n = a.length, t = new Array(n), z = a[n - 1], i = n - 1; + t[i] = z; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z); + return t; // Array + } + }); +}); diff --git a/js/dojo/dojox/lang/functional/sequence.js b/js/dojo/dojox/lang/functional/sequence.js new file mode 100644 index 0000000..3ba4d9e --- /dev/null +++ b/js/dojo/dojox/lang/functional/sequence.js @@ -0,0 +1,39 @@ +//>>built +define("dojox/lang/functional/sequence", ["dojo/_base/lang", "./lambda"], function(lang, df){ + +// This module adds high-level functions and related constructs: +// - sequence generators + +// If you want more general sequence builders check out listcomp.js and +// unfold() (in fold.js). + +// Defined methods: +// - take any valid lambda argument as the functional argument + +/*===== + var df = dojox.lang.functional; + =====*/ + + lang.mixin(df, { + // sequence generators + repeat: function(/*Number*/ n, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by repeatedly applying a unary function N times + // with a seed value Z. N should be greater than 0. + o = o || dojo.global; f = df.lambda(f); + var t = new Array(n), i = 1; + t[0] = z; + for(; i < n; t[i] = z = f.call(o, z), ++i); + return t; // Array + }, + until: function(/*Function|String|Array*/ pr, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by repeatedly applying a unary function with + // a seed value Z until the predicate is satisfied. + o = o || dojo.global; f = df.lambda(f); pr = df.lambda(pr); + var t = []; + for(; !pr.call(o, z); t.push(z), z = f.call(o, z)); + return t; // Array + } + }); + + return df; +}); diff --git a/js/dojo/dojox/lang/functional/tailrec.js b/js/dojo/dojox/lang/functional/tailrec.js new file mode 100644 index 0000000..442e2be --- /dev/null +++ b/js/dojo/dojox/lang/functional/tailrec.js @@ -0,0 +1,120 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/tailrec", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda,dojox/lang/functional/util"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.tailrec"); + +dojo.require("dojox.lang.functional.lambda"); +dojo.require("dojox.lang.functional.util"); + +// This module provides recursion combinators: +// - a tail recursion combinator. + +// Acknoledgements: +// - recursion combinators are inspired by Manfred von Thun's article +// "Recursion Theory and Joy" +// (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html) + +// Notes: +// - recursion combinators produce a function, which implements +// their respective recusion patterns. String lambdas are inlined, if possible. + +(function(){ + var df = dojox.lang.functional, inline = df.inlineLambda, _x ="_x"; + + df.tailrec = function( + /*Function|String|Array*/ cond, + /*Function|String|Array*/ then, + /*Function|String|Array*/ before){ + // summary: + // Generates a function for the tail recursion pattern. This is the simplified + // version of the linear recursive combinator without the "after" function, + // and with the modified "before" function. All parameter functions are called + // in the context of "this" object. + // cond: + // The lambda expression, which is used to detect the termination of recursion. + // It accepts the same parameter as the generated recursive function itself. + // This function should return "true", if the recursion should be stopped, + // and the "then" part should be executed. Otherwise the recursion will proceed. + // then: + // The lambda expression, which is called upon termination of the recursion. + // It accepts the same parameters as the generated recursive function itself. + // The returned value will be returned as the value of the generated function. + // before: + // The lambda expression, which is called before the recursive step. + // It accepts the same parameter as the generated recursive function itself, + // and returns an array of arguments for the next recursive call of + // the generated function. + + var c, t, b, cs, ts, bs, dict1 = {}, dict2 = {}, + add2dict = function(x){ dict1[x] = 1; }; + if(typeof cond == "string"){ + cs = inline(cond, _x, add2dict); + }else{ + c = df.lambda(cond); + cs = "_c.apply(this, _x)"; + dict2["_c=_t.c"] = 1; + } + if(typeof then == "string"){ + ts = inline(then, _x, add2dict); + }else{ + t = df.lambda(then); + ts = "_t.t.apply(this, _x)"; + } + if(typeof before == "string"){ + bs = inline(before, _x, add2dict); + }else{ + b = df.lambda(before); + bs = "_b.apply(this, _x)"; + dict2["_b=_t.b"] = 1; + } + var locals1 = df.keys(dict1), locals2 = df.keys(dict2), + f = new Function([], "var _x=arguments,_t=_x.callee,_c=_t.c,_b=_t.b".concat( // Function + locals1.length ? "," + locals1.join(",") : "", + locals2.length ? ",_t=_x.callee," + locals2.join(",") : t ? ",_t=_x.callee" : "", + ";for(;!", + cs, + ";_x=", + bs, + ");return ", + ts + )); + if(c){ f.c = c; } + if(t){ f.t = t; } + if(b){ f.b = b; } + return f; + }; +})(); + +/* +For documentation only: + +1) The original recursive version: + +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); + }; +}; + +2) The original iterative version (before minification and inlining): + +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); + }; +}; + +*/ +}); diff --git a/js/dojo/dojox/lang/functional/util.js b/js/dojo/dojox/lang/functional/util.js new file mode 100644 index 0000000..57c4fe5 --- /dev/null +++ b/js/dojo/dojox/lang/functional/util.js @@ -0,0 +1,51 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/util", ["dijit","dojo","dojox","dojo/require!dojox/lang/functional/lambda"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.util"); + +dojo.require("dojox.lang.functional.lambda"); + +// This module provides helpers: +// - inlining string lambda functions. + +(function(){ + var df = dojox.lang.functional; + + dojo.mixin(df, { + inlineLambda: function(/*String*/ lambda, /*String|Array*/ init, /*Function?*/ add2dict){ + // summary: + // Creates the inlined version of a string lambda. + // lambda: + // The String variable representing the lambda function. + // init: + // Conveys how to initialize parameters. If it is a String, then the apply() method + // would be emulated treating "init" as a list of input parameters. + // It it is an Array, then the call() method is emulated treating array members + // as input parameters. + // add2dict: + // The optional function, which is used to record names of lambda parameters. + // If supplied, this function is called with a name of every parameter. + + var s = df.rawLambda(lambda); + if(add2dict){ + df.forEach(s.args, add2dict); + } + var ap = typeof init == "string", // apply or call? + n = ap ? s.args.length : Math.min(s.args.length, init.length), + a = new Array(4 * n + 4), i, j = 1; + for(i = 0; i < n; ++i){ + a[j++] = s.args[i]; + a[j++] = "="; + a[j++] = ap ? init + "[" + i + "]": init[i]; + a[j++] = ","; + } + a[0] = "("; + a[j++] = "("; + a[j++] = s.body; + a[j] = "))"; + return a.join(""); // String + } + }); +})(); + +}); diff --git a/js/dojo/dojox/lang/functional/zip.js b/js/dojo/dojox/lang/functional/zip.js new file mode 100644 index 0000000..7b721f8 --- /dev/null +++ b/js/dojo/dojox/lang/functional/zip.js @@ -0,0 +1,45 @@ +//>>built +// wrapped by build app +define("dojox/lang/functional/zip", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.functional.zip"); + +// This module adds high-level functions and related constructs: +// - zip combiners + +// Defined methods: +// - operate on dense arrays + +(function(){ + var df = dojox.lang.functional; + + dojo.mixin(df, { + // combiners + zip: function(){ + // summary: returns an array of arrays, where the i-th array + // contains the i-th element from each of the argument arrays. + // description: This is the venerable zip combiner (for example, + // see Python documentation for general details). The returned + // array is truncated to match the length of the shortest input + // array. + var n = arguments[0].length, m = arguments.length, i = 1, t = new Array(n), j, p; + for(; i < m; n = Math.min(n, arguments[i++].length)); + for(i = 0; i < n; ++i){ + p = new Array(m); + for(j = 0; j < m; p[j] = arguments[j][i], ++j); + t[i] = p; + } + return t; // Array + }, + unzip: function(/*Array*/ a){ + // summary: similar to dojox.lang.functional.zip(), but takes + // a single array of arrays as the input. + // description: This function is similar to dojox.lang.functional.zip() + // and can be used to unzip objects packed by + // dojox.lang.functional.zip(). It is here mostly to provide + // a short-cut for the different method signature. + return df.zip.apply(null, a); // Array + } + }); +})(); + +}); |
