[英]tail call optimization javascript
注意:這僅是為了學習和改善自己。 我知道可用的數組排序方法。 我只是想降低TCO的基礎知識。
當前嘗試使用遞歸進行排序算法。 但是,當我嘗試處理大型數據集(+4000個對象)時,仍然出現堆棧溢出錯誤。 我正在嘗試實施TCO。 我對這個概念還很陌生,但是我想知道它的要旨。 但是,我仍然收到堆棧溢出錯誤。
const sort = (arr, counter) => {
if (!counter) {
counter = arr.length - 1;
}
for (let n = 1; n <= counter; n++) {
if(arr[n - 1] < arr[n]) {
let placeHolder = arr[n];
arr[n] = arr[n - 1];
arr[n - 1] = placeHolder;
}
}
counter -= 1;
return counter === 0 ? arr : sort(arr, counter);
};
function sortRecursive(arr) {
return sort(arr);
}
更新:
我設法使其正常運行,但是我不太明白為什么。 我設法毫無問題地處理了100,000次遞歸。 我必須移動檢查計數器是否已定義的布爾值。 但是,我不太明白為什么它能起作用。
const sort = (arr, counter) => {
if (!counter) {
counter = arr.length - 1;
}
for (let n = 1; n <= counter; n++) {
if(arr[n - 1] < arr[n]) {
let placeHolder = arr[n];
arr[n] = arr[n - 1];
arr[n - 1] = placeHolder;
}
}
counter -= 1;
if (counter === 0) {
return arr;
} else {
return sort(arr, counter);
}
};
function sortRecursive(arr) {
return sort(arr, arr.length - 1);
}
輸出:
let firstArr = [];
let secondArr = [];
for (let x = 0; x < 100000; x++) {
firstArr.push(Math.ceil(Math.random() * 100000));
secondArr.push(Math.ceil(Math.random() * 100000));
}
sortRecursive(firstArr);
//Array[100000]
您可能知道,尾調用優化是一種編譯器技術,可以通過不為每個遞歸調用分配更多的內存來使程序無限遞歸。
Javascript當前尚未進行尾調用優化,但是語言規范的ES2015標准包括TCO。 每次函數使用Javascript調用自身時,都會創建一個新的堆棧框架,分配新的內存,因此它最終將耗盡並崩潰。
有避免這種情況的技術,包括蹦床和不使用遞歸循環。 但是目前您不能無限遞歸地使用Javascript。
為什么需要遞歸(因為它超過了每個調用堆棧,所以它永遠無法使用4000+個元素)? 你不能做:
const sort = (arr) => {
var counter = arr.length;
while(counter-->0){
for (let n = 1; n <= counter; n++) {
if(arr[n - 1] < arr[n]) {
let placeHolder = arr[n];
arr[n] = arr[n - 1];
arr[n - 1] = placeHolder;
}
}
}
return arr;
}
如果您想進行某種遞歸,則可以使用qeue來保持堆棧為空(需要傳遞回調):
setTimeout(sort,0,arr,counter);//instead of sort(arr,counter);
順便說一句,更容易,更快(因為它是本地實現的):
arr.sort((a,b)=>a-b);
您確定要進行尾聲優化嗎?
這是使用更新后的代碼進行的測試。 我更改的唯一內容是:
'use strict';
將代碼置於嚴格模式。 某些將來支持TCO的瀏覽器可能要求使用嚴格模式才能使用TCO。 console.trace()
以在每個sort()
調用上打印調用堆棧。 Math.floor()
而不是Math.ceil()
。 運行代碼片段之前,請打開開發者控制台,並觀察調用堆棧的蹤跡。
我在最新版本的Chrome 59.0.3071.109,Firefox 54.0和Edge 15.15063中對此進行了測試。 來自所有這些堆棧的堆棧跟蹤顯示了每次調用時調用堆棧的增長,表明沒有尾部調用優化。
只是為了踢球,我還在length = 100000
Chrome中嘗試了一下。 它運行了很長的時間(可能是一分鍾左右),然后在堆棧達到大約10257個調用的深度時,由於堆棧溢出而失敗。 為了進行比較,標准sort( function( a, b ) { return b - a; } )
在大約5秒鍾內完成。
這是一篇有關JavaScript尾部調用優化和相關主題的好文章 。 文章提到的一件事是,您可以通過使用--harmony_tailcalls
命令行開關以及'use strict';
來在node.js中獲得TCO 'use strict';
。
'use strict'; const sort = (arr, counter) => { console.trace(); if (!counter) { counter = arr.length - 1; } for (let n = 1; n <= counter; n++) { if(arr[n - 1] < arr[n]) { let placeHolder = arr[n]; arr[n] = arr[n - 1]; arr[n - 1] = placeHolder; } } counter -= 1; if (counter === 0) { return arr; } else { return sort(arr, counter); } }; function sortRecursive(arr) { return sort(arr, arr.length - 1); } let firstArr = []; let length = 10; for (let x = 0; x < length; x++) { firstArr.push(Math.floor(Math.random() * length)); } console.clear(); sortRecursive(firstArr); //firstArr.sort( function( a, b ) { return b - a } ); console.log( firstArr );
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.