迎接ECMAScript 6, 使用尾遞歸

尾調(diào)用师坎,是指函數(shù)內(nèi)部的最后一個動作是函數(shù)調(diào)用涩惑。該調(diào)用的返回值射窒,直接返回給函數(shù)渴频。

Example:

function sum(x) {
    return sum(x + 1);
}

這里的sum()內(nèi)部的sum就是屬于尾調(diào)用芽丹,ta所返回的值直接返回給調(diào)用ta的上層sum()函數(shù)。

function sum(x) {
    return 1 + sum(x + 1);
}

這里的sum()內(nèi)部的sum就不屬于尾調(diào)用卜朗,ta所返回的值在返回給調(diào)用ta的上層sum()函數(shù)之前拔第,需要先跟1計算和,然后再返回场钉。

尾調(diào)用和非尾調(diào)用有什么差異蚊俺?

編寫一個求和函數(shù),有大致3種方式:

  • 循環(huán)

    function sum(x) {
        var result = x;
        while (x >= 1) {
            result += --x;
        }
        return result;
    }
    

    循環(huán)自然是速度和性能最好的逛万,但是在編寫復(fù)雜的代碼時泳猬,循環(huán)往往模塊化很差,可讀性很差宇植,而且無法體現(xiàn)數(shù)學(xué)上的描述得封。

  • 普通遞歸

    function sum(x) {
        if (x === 1) {
            return 1;
        }
        return x + sum(--x);
    }
    

    普通遞歸時,內(nèi)存需要記錄調(diào)用的堆棧所出的深度和位置信息指郁。在最底層計算返回值忙上,再根據(jù)記錄的信息,跳回上一層級計算闲坎,然后再跳回更高一層疫粥,依次運行洋腮,直到最外層的調(diào)用函數(shù)。在cpu計算和內(nèi)存會消耗很多手形,而且當(dāng)深度過大時啥供,會出現(xiàn)堆棧溢出。

    比如库糠,計算sum(5)的時候伙狐,其計算過程是這樣的:

    sum(5)
    (5 + sum(4))
    (5 + (4 + sum(3)))
    (5 + (4 + (3 + sum(2))))
    (5 + (4 + (3 + (2 + sum(1)))))
    (5 + (4 + (3 + (2 + 1))))
    (5 + (4 + (3 + 3)))
    (5 + (4 + 6))
    (5 + 10)
    15

    在計算的過程中,堆棧一直不停的記錄每一層次的詳細信息瞬欧,以確保該層次的操作完成贷屎,能返回到上一層次。

    通過尾遞歸艘虎,可以取消過多的堆棧記錄唉侄,而只記錄最外層和當(dāng)前層的信息,完成計算后野建,立刻返回到最上層属划。從而減少cpu計算和內(nèi)存消耗。

  • 尾遞歸

    function sum(x, total) {
        if (x === 1) {
            return x + total;
        }
        return sum(x - 1, x + total);
    }
    

    這個函數(shù)更具有數(shù)學(xué)描述性:

    • 如果輸入值是1 => 當(dāng)前計算數(shù)1 + 上一次計算的和total
    • 如果輸入值是x => 當(dāng)前計算數(shù)x + 上一次計算的和total

    計算sum(5, 0)的時候候生,其過程是這樣的:

    sum(5, 0)
    sum(4, 5)
    sum(3, 9)
    sum(2, 12)
    sum(1, 14)
    15

    整個計算過程是線性的同眯,調(diào)用一次sum(x, total)后,會進入下一個棧唯鸭,相關(guān)的數(shù)據(jù)信息和跟隨進入须蜗,不再放在堆棧上保存。當(dāng)計算完最后的值之后目溉,直接返回到最上層的sum(5,0)明肮。

    這能有效的防止堆棧溢出。

    而且最happy的是缭付,在ECMAScript 6柿估,我們將迎來尾遞歸優(yōu)化,享受只有LISP HASKELL這些古典函數(shù)式語言才擁有的能力蛉腌。通過尾遞歸優(yōu)化官份,javascript代碼在解釋成機器碼的時候,將會向while看起烙丛,也就是說舅巷,同時擁有數(shù)學(xué)表達能力和while的效能。

使用尾遞歸

這里有一個使用尾遞歸提取絕對文件名的例子河咽,可以來展示下尾遞歸的魅力钠右。這個函數(shù)需要輸入一個(或幾個)目錄名和當(dāng)前所在的文件位置,然后會遞歸提取目錄下的所有文件名忘蟹,放入一個棧中飒房。

var FS = require('fs');
var PATH = require('path');

function readSync(paths, i) {
    if (i >= 0 && i < paths.length) {
        var stats = FS.statSync(paths[i]);
        if (stats.isFile()) {
            return readSync(paths, ++i);
        } else if (stats.isDirectory()) {
            var newPaths = FS.readdirSync(paths[i]).map(function (path) {
                return PATH.join(paths[i],path);
            });
            return readSync(paths.slice(0, i).concat(newPaths, 
                                                     paths.slice(i + 1)), 
                            i);
        } else {
            return readSync(paths.slice(0, i).concat(paths.slice(i + 1)), 
                            i);
        }
    } else {
        return paths;
    }
}

測試一下搁凸,這個函數(shù)可以在服務(wù)器啟動時,提取某一個(或者幾個)目錄內(nèi)的所有文件狠毯,然后用于編譯或者是其他的操作护糖。

readSync([PATH.join(__dirname, './tpls')], 0).forEach(function (path) {
    console.log(path);
});
readSync([PATH.join(__dirname, './tpls'), PATH.join(__dirname, './pages')], 0).forEach(function (path) {
    console.log(path);
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嚼松,隨后出現(xiàn)的幾起案子嫡良,更是在濱河造成了極大的恐慌,老刑警劉巖献酗,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寝受,死亡現(xiàn)場離奇詭異,居然都是意外死亡罕偎,警方通過查閱死者的電腦和手機很澄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颜及,“玉大人甩苛,你說我怎么就攤上這事∑饔瑁” “怎么了浪藻?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乾翔。 經(jīng)常有香客問我,道長施戴,這世上最難降的妖魔是什么反浓? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮赞哗,結(jié)果婚禮上雷则,老公的妹妹穿的比我還像新娘。我一直安慰自己肪笋,他們只是感情好月劈,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著藤乙,像睡著了一般猜揪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坛梁,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天而姐,我揣著相機與錄音,去河邊找鬼划咐。 笑死拴念,一個胖子當(dāng)著我的面吹牛钧萍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播政鼠,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼风瘦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了公般?” 一聲冷哼從身側(cè)響起万搔,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俐载,沒想到半個月后蟹略,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡遏佣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年挖炬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片状婶。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡意敛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出膛虫,到底是詐尸還是另有隱情草姻,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布稍刀,位于F島的核電站撩独,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏账月。R本人自食惡果不足惜综膀,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望局齿。 院中可真熱鬧剧劝,春花似錦、人聲如沸抓歼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谣妻。三九已至萄喳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拌禾,已是汗流浹背取胎。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闻蛀。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓匪傍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親觉痛。 傳聞我的和親對象是個殘疾皇子役衡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容