摘要:而且最的是,在,我們將迎來尾遞歸優(yōu)化,享受只有這些古典函數(shù)式語言才擁有的能力。使用尾遞歸這里有一個使用尾遞歸提取絕對文件名的例子,可以來展示下尾遞歸的魅力。
尾調(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
在計算的過程中,堆棧一直不停的記錄每一層次的詳細(xì)信息,以確保該層次的操作完成,能返回到上一層次。
通過尾遞歸,可以取消過多的堆棧記錄,而只記錄最外層和當(dāng)前層的信息,完成計算后,立刻返回到最上層。從而減少 cpu 計算和內(nèi)存消耗。
尾遞歸
function sum(x, total) { if (x === 1) { return x + total; } return sum(--x, 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é)表達(dá)能力和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); });
文章來源:http://www.jianshu.com/p/269ba1ba1644
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/85386.html
摘要:遞歸函數(shù)就是會直接或者間接地調(diào)用自身的一種函數(shù)。一般來說,一個遞歸函數(shù)調(diào)用自身去解決它的子問題。書上第二個例子是說遞歸函數(shù)可以非常高效率的操作樹形結(jié)構(gòu),比如。有一些語言提供了尾遞歸的優(yōu)化。好運的是,給我們帶來了尾遞歸,詳細(xì)迎接使用尾遞歸。 遞歸函數(shù)就是會直接或者間接地調(diào)用自身的一種函數(shù)。遞歸是一種強大的編程技術(shù),它把一問題分解為一組相似的子問題,每一個都用一個尋常解去解決。一般來...
摘要:遞歸函數(shù)還會受到瀏覽器調(diào)用棧的大小的限制。雖然迭代也會導(dǎo)致性能問題,但是使用優(yōu)化的循環(huán)就可以代替長時間運行的遞歸函數(shù),可以提高新能,因為運行一個循環(huán)比反復(fù)調(diào)用一個函數(shù)的開銷要小。 本文章記錄本人在深入學(xué)習(xí)js循環(huán)中看書理解到的一些東西,加深記憶和并且整理記錄下來,方便之后的復(fù)習(xí)。 選擇正確的循環(huán)體 在大部分編程語言中,代碼執(zhí)行的時間多數(shù)消耗在循環(huán)的執(zhí)行上。 js定義了4種...
摘要:返回空字符串,返回將一個具名函數(shù)賦值給一個變量,則和的屬性都返回這個具名函數(shù)原本的名字。不可以使用對象,該對象在函數(shù)體內(nèi)不存在。等到運行結(jié)束,將結(jié)果返回到,的調(diào)用幀才會消失。 1 函數(shù)參數(shù)的默認(rèn)值 ES6允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面: function log(x = message.,y = duration infomation.) { consol...
摘要:中尾遞歸優(yōu)化支持尾遞歸優(yōu)化如果一個函數(shù)的最后一個操作是函數(shù)調(diào)用,那么將會用跳轉(zhuǎn)而不是子調(diào)用。自從年雙十一正式上線,累計處理了億錯誤事件,付費客戶有陽光保險核桃編程荔枝掌門對微脈青團社等眾多知名企業(yè)。 譯者按: 有時候會遇到Maximum call stack size exceeded的問題,本文教你stack size的計算方法。 原文: The maximum call stac...
摘要:前言本章介紹函數(shù)的擴展。形式為變量名,函數(shù)的最后一個命名參數(shù)以為前綴。規(guī)定只要函數(shù)參數(shù)使用了默認(rèn)值解構(gòu)賦值或者擴展運算符,那么函數(shù)內(nèi)部就不能顯式設(shè)定為嚴(yán)格模式,否則會報錯。箭頭函數(shù)不能用作構(gòu)造函數(shù)。尾遞歸函數(shù)調(diào)用自身,稱為遞歸。 前言本章介紹函數(shù)的擴展。有些不常用的知識了解即可。本章原文鏈接:函數(shù)的擴展。函數(shù)參...
閱讀 4445·2021-09-24 09:47
閱讀 1256·2021-09-03 10:33
閱讀 2135·2019-08-30 11:13
閱讀 1081·2019-08-30 10:49
閱讀 1808·2019-08-29 16:13
閱讀 2103·2019-08-29 11:28
閱讀 3146·2019-08-26 13:31
閱讀 3699·2019-08-23 17:14