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>
這種蹦床方法本質上是實現尾遞迴的輔助函式。這將所有巢狀的函式呼叫轉換為順序的函式呼叫,從而避免堆疊溢位問題。
蹦床函式只是將遞迴程式碼包裝在迴圈中,並逐段呼叫該遞迴函式,直到沒有剩餘的遞迴呼叫。
結論
在本文中,我們討論了蹦床函式的概念。我們從介紹蹦床函式開始,並透過各種程式碼示例將其與尾呼叫遞迴或最佳化進行了比較。希望您學到了很多並樂在其中。
資料結構
網路
關係型資料庫管理系統 (RDBMS)
作業系統
Java
iOS
HTML
CSS
Android
Python
C語言程式設計
C++
C#
MongoDB
MySQL
Javascript
PHP