[483] | 1 | define(["../..", "dojo/_base/lang", "dojo/_base/array"], function(dojox, lang, arr){ |
---|
| 2 | var df = lang.getObject("lang.functional", true, dojox); |
---|
| 3 | |
---|
| 4 | // This module adds high-level functions and related constructs: |
---|
| 5 | // - anonymous functions built from the string |
---|
| 6 | |
---|
| 7 | // Acknowledgements: |
---|
| 8 | // - lambda() is based on work by Oliver Steele |
---|
| 9 | // (http://osteele.com/sources/javascript/functional/functional.js) |
---|
| 10 | // which was published under MIT License |
---|
| 11 | |
---|
| 12 | // Notes: |
---|
| 13 | // - lambda() produces functions, which after the compilation step are |
---|
| 14 | // as fast as regular JS functions (at least theoretically). |
---|
| 15 | |
---|
| 16 | // Lambda input values: |
---|
| 17 | // - returns functions unchanged |
---|
| 18 | // - converts strings to functions |
---|
| 19 | // - converts arrays to a functional composition |
---|
| 20 | |
---|
| 21 | var lcache = {}; |
---|
| 22 | |
---|
| 23 | // split() is augmented on IE6 to ensure the uniform behavior |
---|
| 24 | var split = "ab".split(/a*/).length > 1 ? String.prototype.split : |
---|
| 25 | function(sep){ |
---|
| 26 | var r = this.split.call(this, sep), |
---|
| 27 | m = sep.exec(this); |
---|
| 28 | if(m && m.index == 0){ r.unshift(""); } |
---|
| 29 | return r; |
---|
| 30 | }; |
---|
| 31 | |
---|
| 32 | var lambda = function(/*String*/ s){ |
---|
| 33 | var args = [], sects = split.call(s, /\s*->\s*/m); |
---|
| 34 | if(sects.length > 1){ |
---|
| 35 | while(sects.length){ |
---|
| 36 | s = sects.pop(); |
---|
| 37 | args = sects.pop().split(/\s*,\s*|\s+/m); |
---|
| 38 | if(sects.length){ sects.push("(function(" + args.join(", ") + "){ return (" + s + "); })"); } |
---|
| 39 | } |
---|
| 40 | }else if(s.match(/\b_\b/)){ |
---|
| 41 | args = ["_"]; |
---|
| 42 | }else{ |
---|
| 43 | var l = s.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m), |
---|
| 44 | r = s.match(/[+\-*\/%&|\^\.=<>!]\s*$/m); |
---|
| 45 | if(l || r){ |
---|
| 46 | if(l){ |
---|
| 47 | args.push("$1"); |
---|
| 48 | s = "$1" + s; |
---|
| 49 | } |
---|
| 50 | if(r){ |
---|
| 51 | args.push("$2"); |
---|
| 52 | s = s + "$2"; |
---|
| 53 | } |
---|
| 54 | }else{ |
---|
| 55 | // the point of the long regex below is to exclude all well-known |
---|
| 56 | // lower-case words from the list of potential arguments |
---|
| 57 | var vars = s. |
---|
| 58 | 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, ""). |
---|
| 59 | match(/([a-z_$][a-z_$\d]*)/gi) || [], t = {}; |
---|
| 60 | arr.forEach(vars, function(v){ |
---|
| 61 | if(!t.hasOwnProperty(v)){ |
---|
| 62 | args.push(v); |
---|
| 63 | t[v] = 1; |
---|
| 64 | } |
---|
| 65 | }); |
---|
| 66 | } |
---|
| 67 | } |
---|
| 68 | return {args: args, body: s}; // Object |
---|
| 69 | }; |
---|
| 70 | |
---|
| 71 | var compose = function(/*Array*/ a){ |
---|
| 72 | return a.length ? |
---|
| 73 | function(){ |
---|
| 74 | var i = a.length - 1, x = df.lambda(a[i]).apply(this, arguments); |
---|
| 75 | for(--i; i >= 0; --i){ x = df.lambda(a[i]).call(this, x); } |
---|
| 76 | return x; |
---|
| 77 | } |
---|
| 78 | : |
---|
| 79 | // identity |
---|
| 80 | function(x){ return x; }; |
---|
| 81 | }; |
---|
| 82 | |
---|
| 83 | lang.mixin(df, { |
---|
| 84 | // lambda |
---|
| 85 | rawLambda: function(/*String*/ s){ |
---|
| 86 | // summary: |
---|
| 87 | // builds a function from a snippet, or array (composing), |
---|
| 88 | // returns an object describing the function; functions are |
---|
| 89 | // passed through unmodified. |
---|
| 90 | // description: |
---|
| 91 | // This method is to normalize a functional representation (a |
---|
| 92 | // text snippet) to an object that contains an array of |
---|
| 93 | // arguments, and a body , which is used to calculate the |
---|
| 94 | // returning value. |
---|
| 95 | return lambda(s); // Object |
---|
| 96 | }, |
---|
| 97 | buildLambda: function(/*String*/ s){ |
---|
| 98 | // summary: |
---|
| 99 | // builds a function from a snippet, returns a string, which |
---|
| 100 | // represents the function. |
---|
| 101 | // description: |
---|
| 102 | // This method returns a textual representation of a function |
---|
| 103 | // built from the snippet. It is meant to be evaled in the |
---|
| 104 | // proper context, so local variables can be pulled from the |
---|
| 105 | // environment. |
---|
| 106 | var l = lambda(s); |
---|
| 107 | return "function(" + l.args.join(",") + "){return (" + l.body + ");}"; // String |
---|
| 108 | }, |
---|
| 109 | lambda: function(/*Function|String|Array*/ s){ |
---|
| 110 | // summary: |
---|
| 111 | // builds a function from a snippet, or array (composing), |
---|
| 112 | // returns a function object; functions are passed through |
---|
| 113 | // unmodified. |
---|
| 114 | // description: |
---|
| 115 | // This method is used to normalize a functional |
---|
| 116 | // representation (a text snippet, an array, or a function) to |
---|
| 117 | // a function object. |
---|
| 118 | if(typeof s == "function"){ return s; } |
---|
| 119 | if(s instanceof Array){ return compose(s); } |
---|
| 120 | if(lcache.hasOwnProperty(s)){ return lcache[s]; } |
---|
| 121 | var l = lambda(s); |
---|
| 122 | return lcache[s] = new Function(l.args, "return (" + l.body + ");"); // Function |
---|
| 123 | }, |
---|
| 124 | clearLambdaCache: function(){ |
---|
| 125 | // summary: |
---|
| 126 | // clears internal cache of lambdas |
---|
| 127 | lcache = {}; |
---|
| 128 | } |
---|
| 129 | }); |
---|
| 130 | |
---|
| 131 | return df; |
---|
| 132 | }); |
---|