lambda.js 4.3 KB

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