1 | define(["../..", "dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array"], function(dojox, dojo, 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 | // Acknoledgements: |
---|
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 + "){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(!(v in t)){ |
---|
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 | s = lambda(s); |
---|
107 | return "function(" + s.args.join(",") + "){return (" + s.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(s in lcache){ return lcache[s]; } |
---|
121 | s = lambda(s); |
---|
122 | return lcache[s] = new Function(s.args, "return (" + s.body + ");"); // Function |
---|
123 | }, |
---|
124 | clearLambdaCache: function(){ |
---|
125 | // summary: |
---|
126 | // clears internal cache of lambdas |
---|
127 | lcache = {}; |
---|
128 | } |
---|
129 | }); |
---|
130 | |
---|
131 | return df; |
---|
132 | }); |
---|