2016-09-18 2 views
1

名前付き再帰関数を呼び出すことができれば、匿名再帰関数を呼び出すことができます。方法がある場合は、私の再帰関数とTCO関数です。ES5の再帰匿名関数にTCO(Tail Call Optimiztion)を適用するには

function recursive(length, callback) { 

    tco((function (i, sum) { 
     var args = arguments; 
     if (i > length) { 
      console.log("break statement"); 
      callback(sum) 
      return sum 
     } else { 
      return args.callee(i + 1, sum + i) 
     } 
    }))(0, 0) 
} 

function tco(f) { 
    var value; 
    var active = false; 
    var accumulated = []; 

    return function accumulator() { 
     accumulated.push(arguments); 

     if (!active) { 
      active = true; 

      while (accumulated.length) { 
       value = f.apply(this, accumulated.shift()); 
      } 

      active = false; 

      return value; 
     } 
    } 
} 
+0

は(TCOを構築あなた自身をコードすることができます)。あなたのコードは絡まっていますが、私は推測してみてください。それはちょっと変わった "トランポリン"なのです。 – zerkms

+0

なぜ再帰に名前付き関数が必要だと思いますか? 'Y'コンビネータを見てください。 – Bergi

+2

これはES2015と何が関係していますか? Btwでは、すべての関数がspec準拠のES6実装でテール再帰のために最適化されているため、何も実装する必要はありません。 – Bergi

答えて

1

末尾呼び出しの最適化

ES6は末尾呼び出しシステム、エンジンの最適化への変更を提案しています。関数が別の関数の最後のステートメントとして呼び出されたときに、末尾呼び出しは次のように、次のとおりです。

function doSomething() { 
    return doSomethingElse(); // tail call 
} 

のECMAScript 6は、strictモードで一定の末尾再帰の呼び出しスタックのサイズを小さくしようとしています。この最適化では、代わりに末尾呼び出しのための新しいスタックフレームを作成するは、現在のスタックフレームはクリアされていれば、以下の条件がを満たしているとして再利用されます。

  1. strictモードをオンにする必要があります。
  2. テールコールでは、現在のスタックフレーム内の変数にアクセスする必要はありません(つまり、関数はクロージャではありません)。
  3. テールコールを返す関数は、テールコールが返った後にこれ以上行うことはできません。
  4. テールコールの結果が関数値として返されます。

おそらく、避けるのが最も難しい状況はクロージャを使用することです。クロージャは、包含スコープ内の変数にアクセスできるため、テールコールの最適化がオフになっている可能性があります。

"use strict"; 

function doSomething() { 
    var num = 1, 
     func =() => num; 

    // not optimized - function is a closure 
    return func(); 
} 

ハーネスTCOの最適化:たとえば

階乗を計算し、この機能を、考えてみましょう:

"use strict"; 
function factorial(n) { 

    if (n <= 1) { 
     return 1; 
    } else { 

     // not optimized - must multiply after returning 
     return n * factorial(n - 1); 
    } 
} 

を機能を最適化するために、あなたは、乗算ことを確認する必要があります最後の関数呼び出しの後には発生しません。

"use strict"; 
function factorial(n, p = 1) { 

    if (n <= 1) { 
     return 1 * p; 
    } else { 
     let result = n * p; 

     // optimized 
     return factorial(n - 1, result); 
    } 
} 

出典:それはコンパイラ/ランタイム最適化手法ではなく、何かあなたですので、私はあなたが任意のコードに名前を付けることができます疑うニコラスZakasによって恐ろしいブック、理解のECMAScript 6

+0

なぜ閉鎖がtcoを防ぐのか分かりませんが、なんらかの理由がありますか?私はそれが仕様にあるとは思わない--tcoは常に*起こらなければならない。クロージャがそれをそれ以上使用しなくなるまで文脈を保存する必要があるということは、スタックフレームが周りにとどまる必要があるということを意味しない。 – Bergi

+0

クロージャのフリー変数はヒープに格納されます。したがって、クロージャーは、その周囲のスコープより長く存続し、宣言されています。その結果、クロージャはTCOを妨げることはありません。 – ftor

+0

私は確かに仕様をチェックし、クロージャに関する何も見つからなかったので、私の返信のソースはNicholas Zakasの本から出てきたので、私は彼にそれについて尋ねました。 – Bamieh

関連する問題