2150 |
mathias |
1 |
if(!dojo._hasResource["dojox.lang.functional"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
|
|
2 |
dojo._hasResource["dojox.lang.functional"] = true;
|
|
|
3 |
dojo.provide("dojox.lang.functional");
|
|
|
4 |
|
|
|
5 |
// This module adds high-level functions and related constructs:
|
|
|
6 |
// - list comprehensions similar to JavaScript 1.7
|
|
|
7 |
// - anonymous functions built from the string
|
|
|
8 |
// - zip combiners
|
|
|
9 |
// - "reduce" family of functions
|
|
|
10 |
// - currying and partial functions
|
|
|
11 |
// - argument pre-processing: mixer and flip
|
|
|
12 |
// - miscellaneous useful functions
|
|
|
13 |
|
|
|
14 |
// Acknoledgements:
|
|
|
15 |
// - parts of this module (most notably lambda, constFun, invoke, pluck, and partial)
|
|
|
16 |
// are based on work by Oliver Steele (http://osteele.com/sources/javascript/functional/functional.js)
|
|
|
17 |
// which was published under MIT License
|
|
|
18 |
// - Simple "maybe" monad was donated by Alex Russell.
|
|
|
19 |
|
|
|
20 |
// Notes:
|
|
|
21 |
// - Dojo provides following high-level functions in dojo/_base/array.js:
|
|
|
22 |
// forEach, map, filter, every, some
|
|
|
23 |
// - These functions implemented with optional lambda expression as a parameter.
|
|
|
24 |
// - missing high-level functions are provided with the compatible API:
|
|
|
25 |
// foldl, foldl1, scanl, scanl1, foldr, foldr1, scanr, scanr1,
|
|
|
26 |
// reduce, reduceRight
|
|
|
27 |
// - lambda() and listcomp() produce functions, which after the compilation step are
|
|
|
28 |
// as fast as regular JS functions (at least theoretically).
|
|
|
29 |
|
|
|
30 |
(function(){
|
|
|
31 |
var d = dojo, df = dojox.lang.functional, g_re = /\bfor\b|\bif\b/gm, empty = {};
|
|
|
32 |
|
|
|
33 |
// split() is augmented on IE6 to ensure the uniform behavior
|
|
|
34 |
var split = "ab".split(/a*/).length > 1 ? String.prototype.split :
|
|
|
35 |
function(sep){
|
|
|
36 |
var r = this.split.call(this, sep),
|
|
|
37 |
m = sep.exec(this);
|
|
|
38 |
if(m && m.index == 0){ r.unshift(""); }
|
|
|
39 |
return r;
|
|
|
40 |
};
|
|
|
41 |
var lambda = function(/*String*/ s){
|
|
|
42 |
var args = [], sects = split.call(s, /\s*->\s*/m);
|
|
|
43 |
if(sects.length > 1){
|
|
|
44 |
while(sects.length){
|
|
|
45 |
s = sects.pop();
|
|
|
46 |
args = sects.pop().split(/\s*,\s*|\s+/m);
|
|
|
47 |
if(sects.length){ sects.push("(function(" + args + "){return (" + s + ")})"); }
|
|
|
48 |
}
|
|
|
49 |
} else if(s.match(/\b_\b/)) {
|
|
|
50 |
args = ["_"];
|
|
|
51 |
} else {
|
|
|
52 |
var l = s.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m),
|
|
|
53 |
r = s.match(/[+\-*\/%&|\^\.=<>!]\s*$/m);
|
|
|
54 |
if(l || r){
|
|
|
55 |
if(l){
|
|
|
56 |
args.push("$1");
|
|
|
57 |
s = "$1" + s;
|
|
|
58 |
}
|
|
|
59 |
if(r){
|
|
|
60 |
args.push("$2");
|
|
|
61 |
s = s + "$2";
|
|
|
62 |
}
|
|
|
63 |
} else {
|
|
|
64 |
var vars = s.
|
|
|
65 |
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|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, "").
|
|
|
66 |
match(/([a-z_$][a-z_$\d]*)/gi) || [];
|
|
|
67 |
var t = {};
|
|
|
68 |
d.forEach(vars, function(v){
|
|
|
69 |
if(!(v in t)){
|
|
|
70 |
args.push(v);
|
|
|
71 |
t[v] = 1;
|
|
|
72 |
}
|
|
|
73 |
});
|
|
|
74 |
}
|
|
|
75 |
}
|
|
|
76 |
return {args: args, body: "return (" + s + ");"}; // Object
|
|
|
77 |
};
|
|
|
78 |
|
|
|
79 |
var listcomp = function(/*String*/ s){
|
|
|
80 |
var frag = s.split(g_re), act = s.match(g_re),
|
|
|
81 |
head = ["var r = [];"], tail = [];
|
|
|
82 |
for(var i = 0; i < act.length;){
|
|
|
83 |
var a = act[i], f = frag[++i];
|
|
|
84 |
if(a == "for" && !/^\s*\(\s*(;|var)/.test(f)){
|
|
|
85 |
f = f.replace(/^\s*\(/, "(var ");
|
|
|
86 |
}
|
|
|
87 |
head.push(a, f, "{");
|
|
|
88 |
tail.push("}");
|
|
|
89 |
}
|
|
|
90 |
return head.join("") + "r.push(" + frag[0] + ");" + tail.join("") + "return r;"; // String
|
|
|
91 |
};
|
|
|
92 |
|
|
|
93 |
var currying = function(/*Object*/ info){
|
|
|
94 |
return function(){ // Function
|
|
|
95 |
if(arguments.length + info.args.length < info.arity){
|
|
|
96 |
return currying({func: info.func, arity: info.arity,
|
|
|
97 |
args: Array.prototype.concat.apply(info.args, arguments)});
|
|
|
98 |
}
|
|
|
99 |
return info.func.apply(this, Array.prototype.concat.apply(info.args, arguments));
|
|
|
100 |
};
|
|
|
101 |
};
|
|
|
102 |
|
|
|
103 |
var identity = function(x){ return x; };
|
|
|
104 |
var compose = function(/*Array*/ a){
|
|
|
105 |
return a.length ? function(){
|
|
|
106 |
var i = a.length - 1, x = df.lambda(a[i]).apply(this, arguments);
|
|
|
107 |
for(--i; i >= 0; --i){ x = df.lambda(a[i]).call(this, x); }
|
|
|
108 |
return x;
|
|
|
109 |
} : identity;
|
|
|
110 |
};
|
|
|
111 |
|
|
|
112 |
d.mixin(df, {
|
|
|
113 |
// lambda
|
|
|
114 |
buildLambda: function(/*String*/ s){
|
|
|
115 |
// summary: builds a function from a snippet, returns a string,
|
|
|
116 |
// which represents the function.
|
|
|
117 |
// description: This method returns a textual representation of a function
|
|
|
118 |
// built from the snippet. It is meant to be evaled in the proper context,
|
|
|
119 |
// so local variables can be pulled from the environment.
|
|
|
120 |
s = lambda(s);
|
|
|
121 |
return "function(" + s.args.join(",") + "){" + s.body + "}"; // String
|
|
|
122 |
},
|
|
|
123 |
lambda: function(/*Function|String|Array*/ s){
|
|
|
124 |
// summary: builds a function from a snippet, or array (composing), returns
|
|
|
125 |
// a function object; functions are passed through unmodified.
|
|
|
126 |
// description: This method is used to normalize a functional representation
|
|
|
127 |
// (a text snippet, an array, or a function) to a function object.
|
|
|
128 |
if(typeof s == "function"){ return s; }
|
|
|
129 |
if(s instanceof Array){ return compose(s); }
|
|
|
130 |
s = lambda(s);
|
|
|
131 |
return new Function(s.args, s.body); // Function
|
|
|
132 |
},
|
|
|
133 |
// sequence generators
|
|
|
134 |
repeat: function(/*Number*/ n, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
135 |
// summary: builds an array by repeatedly applying a unary function N times
|
|
|
136 |
// with a seed value Z.
|
|
|
137 |
o = o || d.global; f = df.lambda(f);
|
|
|
138 |
var t = new Array(n);
|
|
|
139 |
t[0] = z;
|
|
|
140 |
for(var i = 1; i < n; t[i] = z = f.call(o, z), ++i);
|
|
|
141 |
return t; // Array
|
|
|
142 |
},
|
|
|
143 |
until: function(/*Function|String|Array*/ pr, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
144 |
// summary: builds an array by repeatedly applying a unary function with
|
|
|
145 |
// a seed value Z until the predicate is satisfied.
|
|
|
146 |
o = o || d.global; f = df.lambda(f); pr = df.lambda(pr);
|
|
|
147 |
var t = [];
|
|
|
148 |
for(; !pr.call(o, z); t.push(z), z = f.call(o, z));
|
|
|
149 |
return t; // Array
|
|
|
150 |
},
|
|
|
151 |
buildListcomp: function(/*String*/ s){
|
|
|
152 |
// summary: builds a function from a text snippet, which represents a valid
|
|
|
153 |
// JS 1.7 list comprehension, returns a string, which represents the function.
|
|
|
154 |
// description: This method returns a textual representation of a function
|
|
|
155 |
// built from the list comprehension text snippet (conformant to JS 1.7).
|
|
|
156 |
// It is meant to be evaled in the proper context, so local variable can be
|
|
|
157 |
// pulled from the environment.
|
|
|
158 |
return "function(){" + listcomp(s) + "}"; // String
|
|
|
159 |
},
|
|
|
160 |
compileListcomp: function(/*String*/ s){
|
|
|
161 |
// summary: builds a function from a text snippet, which represents a valid
|
|
|
162 |
// JS 1.7 list comprehension, returns a function object.
|
|
|
163 |
// description: This method returns a function built from the list
|
|
|
164 |
// comprehension text snippet (conformant to JS 1.7). It is meant to be
|
|
|
165 |
// reused several times.
|
|
|
166 |
return new Function([], listcomp(s)); // Function
|
|
|
167 |
},
|
|
|
168 |
listcomp: function(/*String*/ s){
|
|
|
169 |
// summary: executes the list comprehension building an array.
|
|
|
170 |
return (new Function([], listcomp(s)))(); // Array
|
|
|
171 |
},
|
|
|
172 |
// classic reduce-class functions
|
|
|
173 |
foldl: function(/*Array*/ a, /*Function*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
174 |
// summary: repeatedly applies a binary function to an array from left
|
|
|
175 |
// to right using a seed value as a starting point; returns the final
|
|
|
176 |
// value.
|
|
|
177 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
178 |
for(var i = 0; i < a.length; z = f.call(o, z, a[i], i, a), ++i);
|
|
|
179 |
return z; // Object
|
|
|
180 |
},
|
|
|
181 |
foldl1: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
182 |
// summary: repeatedly applies a binary function to an array from left
|
|
|
183 |
// to right; returns the final value.
|
|
|
184 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
185 |
var z = a[0];
|
|
|
186 |
for(var i = 1; i < a.length; z = f.call(o, z, a[i], i, a), ++i);
|
|
|
187 |
return z; // Object
|
|
|
188 |
},
|
|
|
189 |
scanl: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
190 |
// summary: repeatedly applies a binary function to an array from left
|
|
|
191 |
// to right using a seed value as a starting point; returns an array
|
|
|
192 |
// of values produced by foldl() at that point.
|
|
|
193 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
194 |
var n = a.length, t = new Array(n + 1);
|
|
|
195 |
t[0] = z;
|
|
|
196 |
for(var i = 0; i < n; z = f.call(o, z, a[i], i, a), t[++i] = z);
|
|
|
197 |
return t; // Array
|
|
|
198 |
},
|
|
|
199 |
scanl1: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
200 |
// summary: repeatedly applies a binary function to an array from left
|
|
|
201 |
// to right; returns an array of values produced by foldl1() at that
|
|
|
202 |
// point.
|
|
|
203 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
204 |
var n = a.length, t = new Array(n), z = a[0];
|
|
|
205 |
t[0] = z;
|
|
|
206 |
for(var i = 1; i < n; z = f.call(o, z, a[i], i, a), t[i++] = z);
|
|
|
207 |
return t; // Array
|
|
|
208 |
},
|
|
|
209 |
foldr: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
210 |
// summary: repeatedly applies a binary function to an array from right
|
|
|
211 |
// to left using a seed value as a starting point; returns the final
|
|
|
212 |
// value.
|
|
|
213 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
214 |
for(var i = a.length; i > 0; --i, z = f.call(o, z, a[i], i, a));
|
|
|
215 |
return z; // Object
|
|
|
216 |
},
|
|
|
217 |
foldr1: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
218 |
// summary: repeatedly applies a binary function to an array from right
|
|
|
219 |
// to left; returns the final value.
|
|
|
220 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
221 |
var n = a.length, z = a[n - 1];
|
|
|
222 |
for(var i = n - 1; i > 0; --i, z = f.call(o, z, a[i], i, a));
|
|
|
223 |
return z; // Object
|
|
|
224 |
},
|
|
|
225 |
scanr: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
226 |
// summary: repeatedly applies a binary function to an array from right
|
|
|
227 |
// to left using a seed value as a starting point; returns an array
|
|
|
228 |
// of values produced by foldr() at that point.
|
|
|
229 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
230 |
var n = a.length, t = new Array(n + 1);
|
|
|
231 |
t[n] = z;
|
|
|
232 |
for(var i = n; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z);
|
|
|
233 |
return t; // Array
|
|
|
234 |
},
|
|
|
235 |
scanr1: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){
|
|
|
236 |
// summary: repeatedly applies a binary function to an array from right
|
|
|
237 |
// to left; returns an array of values produced by foldr1() at that
|
|
|
238 |
// point.
|
|
|
239 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
240 |
var n = a.length, t = new Array(n), z = a[n - 1];
|
|
|
241 |
t[n - 1] = z;
|
|
|
242 |
for(var i = n - 1; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z);
|
|
|
243 |
return t; // Array
|
|
|
244 |
},
|
|
|
245 |
// JS 1.6 standard array functions, which can take a lambda as a parameter.
|
|
|
246 |
// Consider using dojo._base.array functions, if you don't need the lambda support.
|
|
|
247 |
filter: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
248 |
// summary: creates a new array with all elements that pass the test
|
|
|
249 |
// implemented by the provided function.
|
|
|
250 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
251 |
var n = a.length, t = [], v;
|
|
|
252 |
for(var i = 0; i < n; ++i){
|
|
|
253 |
v = a[i];
|
|
|
254 |
if(f.call(o, v, i, a)){ t.push(v); }
|
|
|
255 |
}
|
|
|
256 |
return t; // Array
|
|
|
257 |
},
|
|
|
258 |
forEach: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
259 |
// summary: executes a provided function once per array element.
|
|
|
260 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
261 |
var n = a.length;
|
|
|
262 |
for(var i = 0; i < n; f.call(o, a[i], i, a), ++i);
|
|
|
263 |
},
|
|
|
264 |
map: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
265 |
// summary: creates a new array with the results of calling
|
|
|
266 |
// a provided function on every element in this array.
|
|
|
267 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
268 |
var n = a.length, t = new Array(n);
|
|
|
269 |
for(var i = 0; i < n; t[i] = f.call(o, a[i], i, a), ++i);
|
|
|
270 |
return t; // Array
|
|
|
271 |
},
|
|
|
272 |
every: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
273 |
// summary: tests whether all elements in the array pass the test
|
|
|
274 |
// implemented by the provided function.
|
|
|
275 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
276 |
var n = a.length;
|
|
|
277 |
for(var i = 0; i < n; ++i){
|
|
|
278 |
if(!f.call(o, a[i], i, a)){
|
|
|
279 |
return false; // Boolean
|
|
|
280 |
}
|
|
|
281 |
}
|
|
|
282 |
return true; // Boolean
|
|
|
283 |
},
|
|
|
284 |
some: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
285 |
// summary: tests whether some element in the array passes the test
|
|
|
286 |
// implemented by the provided function.
|
|
|
287 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
288 |
var n = a.length;
|
|
|
289 |
for(var i = 0; i < n; ++i){
|
|
|
290 |
if(f.call(o, a[i], i, a)){
|
|
|
291 |
return true; // Boolean
|
|
|
292 |
}
|
|
|
293 |
}
|
|
|
294 |
return false; // Boolean
|
|
|
295 |
},
|
|
|
296 |
// JS 1.8 standard array functions, which can take a lambda as a parameter.
|
|
|
297 |
reduce: function(/*Array*/ a, /*Function*/ f, /*Object?*/ z){
|
|
|
298 |
// summary: apply a function simultaneously against two values of the array
|
|
|
299 |
// (from left-to-right) as to reduce it to a single value.
|
|
|
300 |
return arguments.length < 3 ? df.foldl1(a, f) : df.foldl(a, f, z); // Object
|
|
|
301 |
},
|
|
|
302 |
reduceRight: function(/*Array*/ a, /*Function*/ f, /*Object?*/ z){
|
|
|
303 |
// summary: apply a function simultaneously against two values of the array
|
|
|
304 |
// (from right-to-left) as to reduce it to a single value.
|
|
|
305 |
return arguments.length < 3 ? df.foldr1(a, f) : df.foldr(a, f, z); // Object
|
|
|
306 |
},
|
|
|
307 |
// currying and partial functions
|
|
|
308 |
curry: function(/*Function|String|Array*/ f, /*Number?*/ arity){
|
|
|
309 |
// summary: curries a function until the arity is satisfied, at
|
|
|
310 |
// which point it returns the calculated value.
|
|
|
311 |
f = df.lambda(f);
|
|
|
312 |
arity = typeof arity == "number" ? arity : f.length;
|
|
|
313 |
return currying({func: f, arity: arity, args: []}); // Function
|
|
|
314 |
},
|
|
|
315 |
arg: {}, // marker for missing arguments
|
|
|
316 |
partial: function(/*Function|String|Array*/ f){
|
|
|
317 |
// summary: creates a function where some arguments are bound, and
|
|
|
318 |
// some arguments (marked as dojox.lang.functional.arg) are will be
|
|
|
319 |
// accepted by the final function in the order they are encountered.
|
|
|
320 |
// description: This method is used to produce partially bound
|
|
|
321 |
// functions. If you want to change the order of arguments, use
|
|
|
322 |
// dojox.lang.functional.mixer() or dojox.lang.functional.flip().
|
|
|
323 |
var a = arguments, args = new Array(a.length - 1), p = [];
|
|
|
324 |
f = df.lambda(f);
|
|
|
325 |
for(var i = 1; i < a.length; ++i){
|
|
|
326 |
var t = a[i];
|
|
|
327 |
args[i - 1] = t;
|
|
|
328 |
if(t == df.arg){
|
|
|
329 |
p.push(i - 1);
|
|
|
330 |
}
|
|
|
331 |
}
|
|
|
332 |
return function(){ // Function
|
|
|
333 |
var t = Array.prototype.slice.call(args, 0); // clone the array
|
|
|
334 |
for(var i = 0; i < p.length; ++i){
|
|
|
335 |
t[p[i]] = arguments[i];
|
|
|
336 |
}
|
|
|
337 |
return f.apply(this, t);
|
|
|
338 |
};
|
|
|
339 |
},
|
|
|
340 |
// argument pre-processing
|
|
|
341 |
mixer: function(/*Function|String|Array*/ f, /*Array*/ mix){
|
|
|
342 |
// summary: changes the order of arguments using an array of
|
|
|
343 |
// numbers mix --- i-th argument comes from mix[i]-th place
|
|
|
344 |
// of supplied arguments.
|
|
|
345 |
f = df.lambda(f);
|
|
|
346 |
return function(){ // Function
|
|
|
347 |
var t = new Array(mix.length);
|
|
|
348 |
for(var i = 0; i < mix.length; ++i){
|
|
|
349 |
t[i] = arguments[mix[i]];
|
|
|
350 |
}
|
|
|
351 |
return f.apply(this, t);
|
|
|
352 |
};
|
|
|
353 |
},
|
|
|
354 |
flip: function(/*Function|String|Array*/ f){
|
|
|
355 |
// summary: changes the order of arguments by reversing their
|
|
|
356 |
// order.
|
|
|
357 |
f = df.lambda(f);
|
|
|
358 |
return function(){ // Function
|
|
|
359 |
// reverse arguments
|
|
|
360 |
var a = arguments, l = a.length - 1, t = new Array(l + 1), i;
|
|
|
361 |
for(i = 0; i <= l; ++i){
|
|
|
362 |
t[l - i] = a[i];
|
|
|
363 |
}
|
|
|
364 |
return f.apply(this, t);
|
|
|
365 |
};
|
|
|
366 |
},
|
|
|
367 |
// combiners
|
|
|
368 |
zip: function(){
|
|
|
369 |
// summary: returns an array of arrays, where the i-th array
|
|
|
370 |
// contains the i-th element from each of the argument arrays.
|
|
|
371 |
// description: This is the venerable zip combiner (for example,
|
|
|
372 |
// see Python documentation for general details). The returned
|
|
|
373 |
// array is truncated to match the length of the shortest input
|
|
|
374 |
// array.
|
|
|
375 |
var n = arguments[0].length, m = arguments.length, i;
|
|
|
376 |
for(i = 1; i < m; n = Math.min(n, arguments[i++].length));
|
|
|
377 |
var t = new Array(n), j;
|
|
|
378 |
for(i = 0; i < n; ++i){
|
|
|
379 |
var p = new Array(m);
|
|
|
380 |
for(j = 0; j < m; p[j] = arguments[j][i], ++j);
|
|
|
381 |
t[i] = p;
|
|
|
382 |
}
|
|
|
383 |
return t; // Array
|
|
|
384 |
},
|
|
|
385 |
unzip: function(/*Array*/ a){
|
|
|
386 |
// summary: similar to dojox.lang.functional.zip(), but takes
|
|
|
387 |
// a single array of arrays as the input.
|
|
|
388 |
// description: This function is similar to dojox.lang.functional.zip()
|
|
|
389 |
// and can be used to unzip objects packed by
|
|
|
390 |
// dojox.lang.functional.zip(). It is here mostly to provide
|
|
|
391 |
// a short-cut for the different method signature.
|
|
|
392 |
return df.zip.apply(null, a); // Array
|
|
|
393 |
},
|
|
|
394 |
// miscelaneous functional adapters
|
|
|
395 |
constFun: function(/*Object*/ x){
|
|
|
396 |
// summary: returns a function, which produces a constant value
|
|
|
397 |
// regardless of supplied parameters.
|
|
|
398 |
return function(){ return x; }; // Function
|
|
|
399 |
},
|
|
|
400 |
invoke: function(/*String*/ m){
|
|
|
401 |
// summary: returns a function, which invokes a method on supplied
|
|
|
402 |
// object using optional parameters.
|
|
|
403 |
return function(/*Object*/ o){ // Function
|
|
|
404 |
return o[m].apply(o, Array.prototype.slice.call(arguments, 1));
|
|
|
405 |
};
|
|
|
406 |
},
|
|
|
407 |
pluck: function(/*String*/ m){
|
|
|
408 |
// summary: returns a function, which returns a named object member.
|
|
|
409 |
return function(/*Object*/ o){ // Function
|
|
|
410 |
return o[m];
|
|
|
411 |
};
|
|
|
412 |
},
|
|
|
413 |
// object helpers
|
|
|
414 |
forIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
415 |
// summary: iterates over all object members skipping members, which
|
|
|
416 |
// are present in the empty object (IE and/or 3rd-party libraries).
|
|
|
417 |
o = o || d.global; f = df.lambda(f);
|
|
|
418 |
for(var i in obj){
|
|
|
419 |
if(i in empty){ continue; }
|
|
|
420 |
f.call(o, obj[i], i, obj);
|
|
|
421 |
}
|
|
|
422 |
},
|
|
|
423 |
forEachReversed: function(/*Array*/ a, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
424 |
// summary: executes a provided function once per array element.
|
|
|
425 |
a = typeof a == "string" ? a.split("") : a; o = o || d.global; f = df.lambda(f);
|
|
|
426 |
for(var i = a.length - 1; i >= 0; f.call(o, a[i], i, a), --i);
|
|
|
427 |
}
|
|
|
428 |
});
|
|
|
429 |
|
|
|
430 |
// monads
|
|
|
431 |
dojo.declare("dojox.lang.functional.MaybeMonad", null, {
|
|
|
432 |
constructor: function(/*Object*/ value){
|
|
|
433 |
// summary: constructs a monad optionally initializing all additional members
|
|
|
434 |
if(arguments.length){
|
|
|
435 |
this.value = value;
|
|
|
436 |
}
|
|
|
437 |
},
|
|
|
438 |
bind: function(/*dojox.lang.functional.Monad*/ monad, /*Function|String|Array*/ f, /*Object?*/ o){
|
|
|
439 |
// summary: this is the classic bind method, which applies a function to a monad,
|
|
|
440 |
// and returns a result as a monad; it is meant to be overwritten to incorporate
|
|
|
441 |
// side effects
|
|
|
442 |
if(!("value" in monad)){
|
|
|
443 |
return new this.constructor(); // dojox.lang.functional.MaybeMonad
|
|
|
444 |
}
|
|
|
445 |
// => possible side-effects go here
|
|
|
446 |
o = o || d.global; f = df.lambda(f);
|
|
|
447 |
return f.call(o, monad.value); // dojox.lang.functional.Monad
|
|
|
448 |
},
|
|
|
449 |
// class-specific methods
|
|
|
450 |
isNothing: function(){
|
|
|
451 |
// summary: check if there is no bound value.
|
|
|
452 |
return !("value" in this); // Boolean
|
|
|
453 |
}
|
|
|
454 |
});
|
|
|
455 |
df.MaybeMonad.returnMonad = function(/*Object*/ value){
|
|
|
456 |
// summary: puts a valye in the Maybe monad.
|
|
|
457 |
return new df.MaybeMonad(value); // dojox.lang.functional.MaybeMonad
|
|
|
458 |
};
|
|
|
459 |
df.MaybeMonad.zero = new df.MaybeMonad();
|
|
|
460 |
})();
|
|
|
461 |
|
|
|
462 |
}
|