tailrec.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.lang.functional.tailrec"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.lang.functional.tailrec"] = true;
  8. dojo.provide("dojox.lang.functional.tailrec");
  9. dojo.require("dojox.lang.functional.lambda");
  10. dojo.require("dojox.lang.functional.util");
  11. // This module provides recursion combinators:
  12. // - a tail recursion combinator.
  13. // Acknoledgements:
  14. // - recursion combinators are inspired by Manfred von Thun's article
  15. // "Recursion Theory and Joy"
  16. // (http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html)
  17. // Notes:
  18. // - recursion combinators produce a function, which implements
  19. // their respective recusion patterns. String lambdas are inlined, if possible.
  20. (function(){
  21. var df = dojox.lang.functional, inline = df.inlineLambda, _x ="_x";
  22. df.tailrec = function(
  23. /*Function|String|Array*/ cond,
  24. /*Function|String|Array*/ then,
  25. /*Function|String|Array*/ before){
  26. // summary:
  27. // Generates a function for the tail recursion pattern. This is the simplified
  28. // version of the linear recursive combinator without the "after" function,
  29. // and with the modified "before" function. All parameter functions are called
  30. // in the context of "this" object.
  31. // cond:
  32. // The lambda expression, which is used to detect the termination of recursion.
  33. // It accepts the same parameter as the generated recursive function itself.
  34. // This function should return "true", if the recursion should be stopped,
  35. // and the "then" part should be executed. Otherwise the recursion will proceed.
  36. // then:
  37. // The lambda expression, which is called upon termination of the recursion.
  38. // It accepts the same parameters as the generated recursive function itself.
  39. // The returned value will be returned as the value of the generated function.
  40. // before:
  41. // The lambda expression, which is called before the recursive step.
  42. // It accepts the same parameter as the generated recursive function itself,
  43. // and returns an array of arguments for the next recursive call of
  44. // the generated function.
  45. var c, t, b, cs, ts, bs, dict1 = {}, dict2 = {},
  46. add2dict = function(x){ dict1[x] = 1; };
  47. if(typeof cond == "string"){
  48. cs = inline(cond, _x, add2dict);
  49. }else{
  50. c = df.lambda(cond);
  51. cs = "_c.apply(this, _x)";
  52. dict2["_c=_t.c"] = 1;
  53. }
  54. if(typeof then == "string"){
  55. ts = inline(then, _x, add2dict);
  56. }else{
  57. t = df.lambda(then);
  58. ts = "_t.t.apply(this, _x)";
  59. }
  60. if(typeof before == "string"){
  61. bs = inline(before, _x, add2dict);
  62. }else{
  63. b = df.lambda(before);
  64. bs = "_b.apply(this, _x)";
  65. dict2["_b=_t.b"] = 1;
  66. }
  67. var locals1 = df.keys(dict1), locals2 = df.keys(dict2),
  68. f = new Function([], "var _x=arguments,_t=_x.callee,_c=_t.c,_b=_t.b".concat( // Function
  69. locals1.length ? "," + locals1.join(",") : "",
  70. locals2.length ? ",_t=_x.callee," + locals2.join(",") : t ? ",_t=_x.callee" : "",
  71. ";for(;!",
  72. cs,
  73. ";_x=",
  74. bs,
  75. ");return ",
  76. ts
  77. ));
  78. if(c){ f.c = c; }
  79. if(t){ f.t = t; }
  80. if(b){ f.b = b; }
  81. return f;
  82. };
  83. })();
  84. /*
  85. For documentation only:
  86. 1) The original recursive version:
  87. var tailrec1 = function(cond, then, before){
  88. var cond = df.lambda(cond),
  89. then = df.lambda(then),
  90. before = df.lambda(before);
  91. return function(){
  92. if(cond.apply(this, arguments)){
  93. return then.apply(this, arguments);
  94. }
  95. var args = before.apply(this, arguments);
  96. return arguments.callee.apply(this, args);
  97. };
  98. };
  99. 2) The original iterative version (before minification and inlining):
  100. var tailrec2 = function(cond, then, before){
  101. var cond = df.lambda(cond),
  102. then = df.lambda(then),
  103. before = df.lambda(before);
  104. return function(){
  105. var args = arguments;
  106. for(; !cond.apply(this, args); args = before.apply(this, args));
  107. return then.apply(this, args);
  108. };
  109. };
  110. */
  111. }