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.
132 lines
4.3 KiB
132 lines
4.3 KiB
define(["../..", "dojo/_base/lang", "dojo/_base/array"], function(dojox, 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 |
|
|
|
// Acknowledgements: |
|
// - 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.join(", ") + "){ 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(!t.hasOwnProperty(v)){ |
|
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. |
|
var l = lambda(s); |
|
return "function(" + l.args.join(",") + "){return (" + l.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(lcache.hasOwnProperty(s)){ return lcache[s]; } |
|
var l = lambda(s); |
|
return lcache[s] = new Function(l.args, "return (" + l.body + ");"); // Function |
|
}, |
|
clearLambdaCache: function(){ |
|
// summary: |
|
// clears internal cache of lambdas |
|
lcache = {}; |
|
} |
|
}); |
|
|
|
return df; |
|
});
|
|
|