在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 問答/HTML/ 關(guān)于尾調(diào)用優(yōu)化的疑問

關(guān)于尾調(diào)用優(yōu)化的疑問

首先這個(gè)TCO尾調(diào)用優(yōu)化在ES6中的嚴(yán)格模式下才有作用,但是對(duì)于這個(gè)函數(shù)優(yōu)化有些疑問

什么是尾調(diào)用優(yōu)化

尾調(diào)用之所以與其他調(diào)用不同,就在于它的特殊的調(diào)用位置。
我們知道,函數(shù)調(diào)用會(huì)在內(nèi)存形成一個(gè)"調(diào)用記錄",又稱"調(diào)用幀"(call frame),保存調(diào)用位置和內(nèi)部變量等信息。如果在函數(shù)A的內(nèi)部調(diào)用函數(shù)B,那么在A的調(diào)用記錄上方,還會(huì)形成一個(gè)B的調(diào)用記錄。等到B運(yùn)行結(jié)束,將結(jié)果返回到A,B的調(diào)用記錄才會(huì)消失。如果函數(shù)B內(nèi)部還調(diào)用函數(shù)C,那就還有一個(gè)C的調(diào)用記錄棧,以此類推。所有的調(diào)用記錄,就形成一個(gè)"調(diào)用棧"(call stack)。
尾調(diào)用由于是函數(shù)的最后一步操作,所以不需要保留外層函數(shù)的調(diào)用記錄,因?yàn)檎{(diào)用位置、內(nèi)部變量等信息都不會(huì)再用到了,只要直接用內(nèi)層函數(shù)的調(diào)用記錄,取代外層函數(shù)的調(diào)用記錄就可以了。

疑問1 來源于鏈接描述

代碼1 未進(jìn)行尾調(diào)用優(yōu)化

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120

代碼2 進(jìn)行了尾調(diào)用優(yōu)化

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

真心是沒看出來哪里進(jìn)行了優(yōu)化,一個(gè)參數(shù)的傳遞哪里體現(xiàn)出了tco,在我看來factorial函數(shù)還是在不斷地調(diào)用自身,形成一個(gè)循環(huán)調(diào)用幀(因?yàn)槊看巫哉{(diào)用都會(huì)用到上一次外層函數(shù)的參數(shù),所以會(huì)形成一個(gè)很龐大的調(diào)用幀紀(jì)錄),但是這個(gè)例子中實(shí)在是沒看出來哪里進(jìn)行了優(yōu)化

疑問2 來源于鏈接描述

代碼1

//使用遞歸將求和過程復(fù)雜化
function sum(x, y) {
    if (y > 0) {
      return sum(x + 1, y - 1);
    } else {
      return x;
    }
}

sum(1, 10); // => 11

代碼2

function sum(x, y) {
    function recur(a, b) {
        if (b > 0) {
            return recur(a + 1, b - 1);
        } else {
            return a;
        }
    }
//尾遞歸即在程序尾部調(diào)用自身,注意這里沒有其他的運(yùn)算
    return recur(x, y);
}

sum(1, 10); // => 11

代碼2 在recur函數(shù)的外層包裹了一個(gè)函數(shù),實(shí)現(xiàn)了大框架上面recur這個(gè)尾調(diào)用不依賴sum這個(gè)外層函數(shù)的參數(shù),所以實(shí)現(xiàn)了recur是sum的尾調(diào)用(recur不依賴sum,不會(huì)存儲(chǔ)sum這個(gè)函數(shù)的調(diào)用紀(jì)錄),但是sum的內(nèi)層函數(shù)recur不還是和代碼1 一樣么,還是在繼續(xù)遞歸,又形成了很龐大的調(diào)用幀紀(jì)錄,所以不明白這樣給遞歸函數(shù)包裹了一層外層函數(shù)有什么意義?或者說這種寫法原理是什么??jī)?nèi)層函數(shù)還是該遞歸遞歸
按我的理解解決遞歸的性能就該類似動(dòng)態(tài)規(guī)劃一般:把每次的執(zhí)行結(jié)果保存在一個(gè)變量中,而每次循環(huán)只是將結(jié)果重新賦值給這個(gè)變量繼續(xù)提供給下一次循環(huán),這樣不就可以避免存儲(chǔ)上一次的調(diào)用幀,防止內(nèi)存溢出
例入:只是一個(gè)優(yōu)化方面,不用仔細(xì)帶入上面兩個(gè)疑問
代碼1

function fib(n){
    if(n < 2){
        return n;
    }else{
        return fib(n - 1) + fib(n - 2);
    }
}
console.log(fib(10));   // 55

代碼2

function fibDyn(n){
    var prev = 1;
    var middle = 1;
    var result = 1;

    for(var i = 2; i < n; i++){
        result = prev + middle;
        prev = middle;
        middle = result;
    }
    return result;
}
fibDyn(10)  // 55
回答
編輯回答
愿如初
尾調(diào)用由于是函數(shù)的最后一步操作,所以不需要保留外層函數(shù)的調(diào)用記錄,因?yàn)檎{(diào)用位置、內(nèi)部變量等信息都不會(huì)再用到了,只要直接用內(nèi)層函數(shù)的調(diào)用記錄,取代外層函數(shù)的調(diào)用記錄就可以了。

代碼1 未進(jìn)行尾調(diào)用優(yōu)化
return n * factorial(n - 1); 在內(nèi)層結(jié)束后還需要再被n乘才能返回

代碼2
return factorial(n - 1, n * total); 內(nèi)層結(jié)束了就結(jié)束了

尾遞歸會(huì)在編譯時(shí)被(一些編譯器)優(yōu)化為循環(huán),所以他不會(huì)出現(xiàn)溢出。最后一個(gè)fibDyn就是尾遞歸在“人手優(yōu)化后”的形式了

2018年5月10日 05:04
編輯回答
久不遇

TCO不依賴遞歸中的一次性中間變量和臨時(shí)函數(shù),這些東西都可以直接被垃圾回收掉

2017年5月12日 11:33