ES6 蹦床函式


在本教程中,我們將主要關注 ES6(ECMAScript 6)中首次出現的蹦床函式。我們將從討論蹦床函式開始,探討使用蹦床函式的必要性,以及使用蹦床函式的優缺點。

現在,讓我們看看蹦床函式。

什麼是蹦床函式?

蹦床只是一種最佳化遞歸併防止在不提供尾呼叫最佳化(例如 JavaScript ES5)的語言中出現堆疊溢位錯誤的機制。為了克服尾呼叫最佳化的這個問題,ES6 版本中引入了蹦床。

蹦床函式本質上是一個圍繞我們的遞迴函式的迴圈。它逐個呼叫遞迴程式碼,直到不再生成遞迴呼叫。如果蹦床函式如此有益,那麼遞迴到底有什麼問題呢?讓我們在本文的下一節中看看。

使用遞迴的問題

首先,讓我們看看什麼是遞迴。遞迴是一個過程,我們使用相同的函式來計算任何結果值,但透過由棧資料結構控制的許多不同的函式呼叫來執行。遞迴簡化了程式碼,對於較大的數量,逐段解決問題。

普通遞迴的問題是,每次遞迴呼叫都會向呼叫棧增加一個棧項,這可以看作是一個呼叫金字塔。這是一個遞迴呼叫階乘函式的示例:

(factorial 4)
(* 4 (factorial 3))
(*4 (*3 (factorial 2)))
(*4 (*3 (*2(factorial 1))))
(*4 (*3 (*2(*1(factorial 0)))))
(*4 (*3 (*2(*1(1)))))
(*4 (*3 (*2(1))))
(*4 (*3 (2)))
(*4 (6))
24

這是一個堆疊視覺化,其中每個垂直短劃線代表一個堆疊幀:

					—|—
				—|—		—|—
			—|—				—|—
		—|—						—|—
	—|—								—|—

問題是堆疊的大小是有限的,堆積這些堆疊幀會導致堆疊溢位。根據其大小,更大的階乘計算可能會導致堆疊溢位。因此,在 C#、JavaScript 等語言中,常規遞迴可能被認為是有風險的。

可以使用尾遞迴解決堆疊溢位的問題。現在,讓我們談談尾遞迴的概念。

為什麼使用蹦床函式?

在學習蹦床函式之前,讓我們詳細瞭解遞迴、尾遞迴的概念以及程式碼示例。

尾遞迴到底是什麼?從本質上講,它進行跳轉並重用先前遞迴呼叫的上下文,因此沒有額外的記憶體成本,而不是將遞迴函式作為返回值呼叫。

這意味著,只要我們在遞迴函式中使用適當的尾遞迴,記憶體使用量就會保持不變,而不是隨著每次遞迴呼叫的記憶體消耗不斷增加。例如,正確實現的階乘函式的尾遞迴實現的記憶體使用情況可能如下所示。

為了更好地理解,讓我們使用遞迴和尾遞迴來查詢數字的階乘。

使用遞迴

示例

<html>  
<body style = "text-align: center; font-size: 20px;">  
   <h2>Using Recursion</h2>
   Enter a number: <input id = "digit">  
   <br><br>  
   <button onclick = "factorial()"> Factorial </button>  
   <p id = "result"></p>  
   <script>  
      function fact(num) {  
         if (num == 0) {  
            return 1;  
         }  
         else {  
            return num * fact( num - 1 );  
         }  
      }  

      function factorial() {  
         var num = document.getElementById("digit").value;  
         var fx = fact(num);  
         document.getElementById("result").innerHTML="The factorial of the number " + num + " is: " + fx ;  
      }  
   </script>  
</body>  
</html> 

如上所示,每個階乘呼叫首先執行。只有這樣,才能將整個數字相乘。為了將其轉換為尾遞迴,我們正在修改函式以將結果作為第二個引數。

使用尾遞迴

示例

<html>  
<body style = "text-align: center; font-size: 20px;">  
   <h2>Using tail recursion</h1>
   Enter a number: <input id = "digit">  
   <br><br>  
   <button onclick = "factorial()"> Factorial </button>  
   <p id = "result"></p>  
   <script>  
      function fact(num,result=1) {  
         if(num === 1) {
            return result;
         } else {
            return fact(num - 1, result * num);
         }
         return result;
      }  

      function factorial() {  
         var num = document.getElementById("digit").value;  
         var fx = fact(num);  
         document.getElementById("result").innerHTML="Thefactorial of the number " + num + " is: " + fx ;  
      }  
   </script>  
</body>  
</html>

這種型別的執行效率更高,因為它需要更少的堆疊操作和物件。

使用蹦床函式

示例

<html>  
<body style = "text-align: center; font-size: 20px;">  
   <h1> Using Trampoline Function </h1>
   Enter a number: <input id = "digit">  
   <br><br>  
   <button onclick = "factorial()"> Factorial </button>  
   <p id = "result"></p>  
   <script>  
      const tram_fn = (fx) => (...arguments) => {
         let result = fx(...arguments);
         while (typeof result === 'function') {
            result = result();
         }
         return result;
      };

      const sumCalculation = (num, sum = 1) => {
         return num === 1 ? sum : () => sumCalculation(num - 1, sum * num);
      };

      function factorial(){
         var num1 = document.getElementById("digit").value;  
         const sumCalculate = tram_fn(sumCalculation);
         document.getElementById("result").innerHTML="The factorial of the number " + num1 + " is: " + sumCalculate(num1);  

      }
   </script>  
</body>  
</html>  

這種蹦床方法本質上是實現尾遞迴的輔助函式。這將所有巢狀的函式呼叫轉換為順序的函式呼叫,從而避免堆疊溢位問題。

蹦床函式只是將遞迴程式碼包裝在迴圈中,並逐段呼叫該遞迴函式,直到沒有剩餘的遞迴呼叫。

結論

在本文中,我們討論了蹦床函式的概念。我們從介紹蹦床函式開始,並透過各種程式碼示例將其與尾呼叫遞迴或最佳化進行了比較。希望您學到了很多並樂在其中。

更新於:2023年1月19日

357 次瀏覽

啟動您的職業生涯

完成課程獲得認證

開始
廣告
© . All rights reserved.