摘要:而且最的是,在,我們將迎來(lái)尾遞歸優(yōu)化,享受只有這些古典函數(shù)式語(yǔ)言才擁有的能力。使用尾遞歸這里有一個(gè)使用尾遞歸提取絕對(duì)文件名的例子,可以來(lái)展示下尾遞歸的魅力。
尾調(diào)用,是指函數(shù)內(nèi)部的最后一個(gè)動(dòng)作是函數(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 計(jì)算和,然后再返回。
尾調(diào)用和非尾調(diào)用有什么差異?
編寫(xiě)一個(gè)求和函數(shù),有大致3種方式:
循環(huán)
function sum(x) { var result = x; while (x >= 1) { result += --x; } return result; }
循環(huán)自然是速度和性能最好的,但是在編寫(xiě)復(fù)雜的代碼時(shí),循環(huán)往往模塊化很差,可讀性很差,而且無(wú)法體現(xiàn)數(shù)學(xué)上的描述。
普通遞歸
function sum(x) { if (x === 1) { return 1; } return x + sum(--x); }
普通遞歸時(shí),內(nèi)存需要記錄調(diào)用的堆棧所出的深度和位置信息。在最底層計(jì)算返回值,再根據(jù)記錄的信息,跳回上一層級(jí)計(jì)算,然后再跳回更高一層,依次運(yùn)行,直到最外層的調(diào)用函數(shù)。在cpu計(jì)算和內(nèi)存會(huì)消耗很多,而且當(dāng)深度過(guò)大時(shí),會(huì)出現(xiàn)堆棧溢出。
比如,計(jì)算 sum(5) 的時(shí)候,其計(jì)算過(guò)程是這樣的:
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
在計(jì)算的過(guò)程中,堆棧一直不停的記錄每一層次的詳細(xì)信息,以確保該層次的操作完成,能返回到上一層次。
通過(guò)尾遞歸,可以取消過(guò)多的堆棧記錄,而只記錄最外層和當(dāng)前層的信息,完成計(jì)算后,立刻返回到最上層。從而減少 cpu 計(jì)算和內(nèi)存消耗。
尾遞歸
function sum(x, total) { if (x === 1) { return x + total; } return sum(--x, x + total); }
這個(gè)函數(shù)更具有數(shù)學(xué)描述性:
如果輸入值是1 => 當(dāng)前計(jì)算數(shù)1 + 上一次計(jì)算的和total
如果輸入值是x => 當(dāng)前計(jì)算數(shù)x + 上一次計(jì)算的和total
計(jì)算 sum(5, 0)的時(shí)候,其過(guò)程是這樣的:
sum(5, 0) sum(4, 5) sum(3, 9) sum(2, 12) sum(1, 14) 15
整個(gè)計(jì)算過(guò)程是線性的,調(diào)用一次 sum(x, total) 后,會(huì)進(jìn)入下一個(gè)棧,相關(guān)的數(shù)據(jù)信息和跟隨進(jìn)入,不再放在堆棧上保存。當(dāng)計(jì)算完最后的值之后,直接返回到最上層的 sum(5,0)。
這能有效的防止堆棧溢出。
而且最happy的是,在ECMAScript 6,我們將迎來(lái)尾遞歸優(yōu)化,享受只有LISP
HASKELL這些古典函數(shù)式語(yǔ)言才擁有的能力。通過(guò)尾遞歸優(yōu)化,javascript代碼在解釋成機(jī)器碼的時(shí)候,將會(huì)向while看起,也就是說(shuō),同時(shí)擁有數(shù)學(xué)表達(dá)能力和while的效能。
使用尾遞歸
這里有一個(gè)使用尾遞歸提取絕對(duì)文件名的例子,可以來(lái)展示下尾遞歸的魅力。這個(gè)函數(shù)需要輸入一個(gè)(或幾個(gè))目錄名和當(dāng)前所在的文件位置,然后會(huì)遞歸提取目錄下的所有文件名,放入一個(gè)棧中。
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; } }
測(cè)試一下,這個(gè)函數(shù)可以在服務(wù)器啟動(dòng)時(shí),提取某一個(gè)(或者幾個(gè))目錄內(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); });
文章來(lái)源:http://www.jianshu.com/p/269ba1ba1644
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85386.html
摘要:遞歸函數(shù)就是會(huì)直接或者間接地調(diào)用自身的一種函數(shù)。一般來(lái)說(shuō),一個(gè)遞歸函數(shù)調(diào)用自身去解決它的子問(wèn)題。書(shū)上第二個(gè)例子是說(shuō)遞歸函數(shù)可以非常高效率的操作樹(shù)形結(jié)構(gòu),比如。有一些語(yǔ)言提供了尾遞歸的優(yōu)化。好運(yùn)的是,給我們帶來(lái)了尾遞歸,詳細(xì)迎接使用尾遞歸。 遞歸函數(shù)就是會(huì)直接或者間接地調(diào)用自身的一種函數(shù)。遞歸是一種強(qiáng)大的編程技術(shù),它把一問(wèn)題分解為一組相似的子問(wèn)題,每一個(gè)都用一個(gè)尋常解去解決。一般來(lái)...
摘要:遞歸函數(shù)還會(huì)受到瀏覽器調(diào)用棧的大小的限制。雖然迭代也會(huì)導(dǎo)致性能問(wèn)題,但是使用優(yōu)化的循環(huán)就可以代替長(zhǎng)時(shí)間運(yùn)行的遞歸函數(shù),可以提高新能,因?yàn)檫\(yùn)行一個(gè)循環(huán)比反復(fù)調(diào)用一個(gè)函數(shù)的開(kāi)銷(xiāo)要小。 本文章記錄本人在深入學(xué)習(xí)js循環(huán)中看書(shū)理解到的一些東西,加深記憶和并且整理記錄下來(lái),方便之后的復(fù)習(xí)。 選擇正確的循環(huán)體 在大部分編程語(yǔ)言中,代碼執(zhí)行的時(shí)間多數(shù)消耗在循環(huán)的執(zhí)行上。 js定義了4種...
摘要:返回空字符串,返回將一個(gè)具名函數(shù)賦值給一個(gè)變量,則和的屬性都返回這個(gè)具名函數(shù)原本的名字。不可以使用對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。等到運(yùn)行結(jié)束,將結(jié)果返回到,的調(diào)用幀才會(huì)消失。 1 函數(shù)參數(shù)的默認(rèn)值 ES6允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫(xiě)在參數(shù)定義的后面: function log(x = message.,y = duration infomation.) { consol...
摘要:中尾遞歸優(yōu)化支持尾遞歸優(yōu)化如果一個(gè)函數(shù)的最后一個(gè)操作是函數(shù)調(diào)用,那么將會(huì)用跳轉(zhuǎn)而不是子調(diào)用。自從年雙十一正式上線,累計(jì)處理了億錯(cuò)誤事件,付費(fèi)客戶有陽(yáng)光保險(xiǎn)核桃編程荔枝掌門(mén)對(duì)微脈青團(tuán)社等眾多知名企業(yè)。 譯者按: 有時(shí)候會(huì)遇到Maximum call stack size exceeded的問(wèn)題,本文教你stack size的計(jì)算方法。 原文: The maximum call stac...
摘要:前言本章介紹函數(shù)的擴(kuò)展。形式為變量名,函數(shù)的最后一個(gè)命名參數(shù)以為前綴。規(guī)定只要函數(shù)參數(shù)使用了默認(rèn)值解構(gòu)賦值或者擴(kuò)展運(yùn)算符,那么函數(shù)內(nèi)部就不能顯式設(shè)定為嚴(yán)格模式,否則會(huì)報(bào)錯(cuò)。箭頭函數(shù)不能用作構(gòu)造函數(shù)。尾遞歸函數(shù)調(diào)用自身,稱(chēng)為遞歸。 前言本章介紹函數(shù)的擴(kuò)展。有些不常用的知識(shí)了解即可。本章原文鏈接:函數(shù)的擴(kuò)展。函數(shù)參...
閱讀 4320·2021-09-24 09:47
閱讀 1192·2021-09-03 10:33
閱讀 2077·2019-08-30 11:13
閱讀 1039·2019-08-30 10:49
閱讀 1762·2019-08-29 16:13
閱讀 2052·2019-08-29 11:28
閱讀 3102·2019-08-26 13:31
閱讀 3638·2019-08-23 17:14