嫩草影院久久99_老司机午夜网站国内精品久久久久久久久_久久夜色精品国产_国产一级做a爰片久久毛片

JavaScript 中匿名函數(shù)的遞歸調(diào)用

如果您想訂閱本博客內(nèi)容,每天自動發(fā)到您的郵箱中, 請點這里

不管是什么編程語言,相信稍微寫過幾行代碼的同學(xué),對遞歸都不會陌生。 以一個簡單的階乘計算為例:

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

我們可以看出,遞歸就是在函數(shù)內(nèi)部調(diào)用對自身的調(diào)用。 那么問題來了,我們知道在Javascript中,有一類函數(shù)叫做匿名函數(shù),沒有名稱,怎么調(diào)用呢?當(dāng)然你可以說,可以把匿名函數(shù)賦值給一個常量:

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

這當(dāng)然是可以的。但是對于一些像,函數(shù)編寫時并不知道自己將要賦值給一個明確的變量的情況時,就會遇到麻煩了。如:

(function(f){
    f(10);
})(function(n){ if (n <= 1) { return 1;
    } else { return n * factorial(n-1);//太依賴于上下文變量名 }
}) //Uncaught ReferenceError: factorial is not defined(…)

那么存不存在一種完全不需要這種給予準(zhǔn)確函數(shù)名(函數(shù)引用變量名)的方式呢?

arguments.callee

我們知道在任何一個function內(nèi)部,都可以訪問到一個叫做arguments的變量。

(function(){console.dir(arguments)})(1,2)

1.png

打印出這個arguments變量的細節(jié),可以看出他是Arguments的一個實例,而且從數(shù)據(jù)結(jié)構(gòu)上來講,他是一個類數(shù)組。他除了類數(shù)組的元素成員和length屬性外,還有一個callee方法。 那么這個callee方法是做什么的呢?我們來看下MDN

callee 是 arguments 對象的屬性。在該函數(shù)的函數(shù)體內(nèi),它可以指向當(dāng)前正在執(zhí)行的函數(shù)。當(dāng)函數(shù)是匿名函數(shù)時,這是很有用的, 比如沒有名字的函數(shù)表達式 (也被叫做”匿名函數(shù)”)。

哈哈,很明顯這就是我們想要的。接下來就是:

(function(f){ console.log(f(10));
})(function(n){ if (n <= 1) { return 1;
    } else { return n * arguments.callee(n-1);
    }
}) //output: 3628800

但是還有一個問題,MDN的文檔里明確指出

警告:在 ECMAScript 第五版 (ES5) 的 嚴(yán)格模式 中禁止使用 arguments.callee()。

哎呀,原來在ES5的use strict;中不給用啊,那么在ES6中,我們換個ES6的arrow function寫寫看:

((f) => console.log(f(10)))( (n) => n <= 1? 1: arguments.callee(n-1)) //Uncaught ReferenceError: arguments is not defined(…)

有一定ES6基礎(chǔ)的同學(xué),估計老早就想說了,箭頭函數(shù)就是個簡寫形式的函數(shù)表達式,并且它擁有詞法作用域的this值(即不會新產(chǎn)生自己作用域下的thisargumentssuper 和 new.target等對象),且都是匿名的。

那怎么辦呢?嘿嘿,我們需要借助一點FP的思想了。

Y組合子

關(guān)于Y Combinator的文章可謂數(shù)不勝數(shù),這個由師從希爾伯特的著名邏輯學(xué)家Haskell B.Curry(Haskell語言就是以他命名的,而函數(shù)式編程語言里面的Curry手法也是以他命名)“發(fā)明”出來的組合算子(Haskell是研究組合邏輯(combinatory logic)的)仿佛有種神奇的魔力,它能夠算出給定lambda表達式(函數(shù))的不動點。從而使得遞歸成為可能。

這里需要告知一個概念不動點組合子

不動點組合子(英語:Fixed-point combinator,或不動點算子)是計算其他函數(shù)的一個不動點的高階函數(shù)。

函數(shù)f的不動點是一個值x使得f(x) = x。例如,0和1是函數(shù) f(x) = x^2 的不動點,因為 0^2 = 0而 1^2 = 1。鑒于一階函數(shù)(在簡單值比如整數(shù)上的函數(shù))的不動點是個一階值,高階函數(shù)f的不動點是另一個函數(shù)g使得f(g) = g。那么,不動點算子是任何函數(shù)fix使得對于任何函數(shù)f都有

f(fix(f)) = fix(f). 不動點組合子允許定義匿名的遞歸函數(shù)。它們可以用非遞歸的lambda抽象來定義.

在無類型lambda演算中眾所周知的(可能是最簡單的)不動點組合子叫做Y組合子。

接下來,我們通過一定的演算推到下這個Y組合子。

// 首先我們定義這樣一個可以用作求階乘的遞歸函數(shù) const fact = (n) => n<=1?1:n*fact(n-1)  
console.log(fact(5)) //120 // 既然不讓這個函數(shù)有名字,我們就先給這個遞歸方法一個叫做self的代號 // 首先是一個接受這個遞歸函數(shù)作為參數(shù)的一個高階函數(shù) const fact_gen = (self) => (n) => n<=1?1:n*self(n-1)  
console.log(fact_gen(fact)(5)) //120 // 我們是將遞歸方法和參數(shù)n,都傳入遞歸方法,得到這樣一個函數(shù) const fact1 = (self, n) => n<=1?1:n*self(self, n-1)  
console.log(fact1(fact1, 5)) //120 // 我們將fact1 柯理化,得到fact2 const fact2 = (self) => (n) => n<=1?1:n*self(self)(n-1)  
console.log(fact2(fact2)(5)) //120 // 驚喜的事發(fā)生了,如果我們將self(self)看做一個整體 // 作為參數(shù)傳入一個新的函數(shù): (g)=> n<= 1? 1: n*g(n-1) const fact3 = (self) => (n) => ((g)=>n <= 1?1:n*g(n-1))(self(self))  
console.log(fact3(fact3)(5)) //120 // fact3 還有一個問題是這個新抽離出來的函數(shù),是上下文有關(guān)的 // 他依賴于上文的n, 所以我們將n作為新的參數(shù) // 重新構(gòu)造出這么一個函數(shù): (g) => (m) => m<=1?1:m*g(m-1) const fact4 = (self) => (n) => ((g) => (m) => m<=1?1:m*g(m-1))(self(self))(n)  
console.log(fact4(fact4)(5)) // 很明顯fact4中的(g) => (m) => m<=1?1:m*g(m-1) 就是 fact_gen // 這就很有意思啦,這個fact_gen上下文無關(guān)了, 可以作為參數(shù)傳入了 const weirdFunc = (func_gen) => (self) => (n) => func_gen(self(self))(n)  
console.log(weirdFunc(fact_gen)(weirdFunc(fact_gen))(5)) //120 // 此時我們就得到了一種Y組合子的形式了 const Y_ = (gen) => (f) => (n)=> gen(f(f))(n) // 構(gòu)造一個階乘遞歸也很easy了 const factorial = Y_(fact_gen)  
console.log(factorial(factorial)(5)) //120 // 但上面這個factorial并不是我們想要的 // 只是一種fact2,fact3,fact4的形式 // 我們肯定希望這個函數(shù)的調(diào)用是factorial(5) // 沒問題,我們只需要把定義一個 f' = f(f) = (f)=>f(f) // eg. const factorial = fact2(fact2) const Y = gen => n => (f=>f(f))(gen)(n)  
console.log(Y(fact2)(5)) //120  console.log(Y(fact3)(5)) //120  console.log(Y(fact4)(5)) //120

推導(dǎo)到這里,是不是已經(jīng)感覺到脊背嗖涼了一下,反正筆者我第一次接觸在康托爾、哥德爾、圖靈——永恒的金色對角線這篇文章里接觸到的時候,整個人瞬間被這種以數(shù)學(xué)語言去表示程序的方式所折服。

來,我們回憶下,我們最終是不是得到了一個不定點算子,這個算子可以找出一個高階函數(shù)的不動點f(Y(f)) = Y(f)。 將一個函數(shù)傳入一個算子(函數(shù)),得到一個跟自己功能一樣,但又并不是自己的函數(shù),這個說法有些拗口,但又味道十足。

好了,我們回到最初的問題,怎么完成匿名函數(shù)的遞歸呢?有了Y組合子就很簡單了:

/*求不動點*/ (f => f(f)) /*以不動點為參數(shù)的遞歸函數(shù)*/ (fact => n => n <= 1 ? 1 : n * fact(fact)(n - 1)) /*遞歸函數(shù)參數(shù)*/ (5) // 120

曾經(jīng)看到過一些說法是”最讓人沮喪是,當(dāng)你推導(dǎo)出它(Y組合子)后,完全沒法兒通過只看它一眼就說出它到底是想干嘛”,而我恰恰認為這就是函數(shù)式編程的魅力,也是數(shù)學(xué)的魅力所在,精簡優(yōu)雅的公式,背后隱藏著復(fù)雜有趣的推導(dǎo)過程。

2.jpg

總結(jié)

務(wù)實點兒講,匿名函數(shù)的遞歸調(diào)用,在日常的js開發(fā)中,用到的真的很少。把這個問題拿出來講,主要是想引出對arguments的一些講解和對Y組合子這個概念的一個普及。

但既然講都講了,我們真的用到的話,該怎么選擇呢?來,我們喜聞樂見的benchmark下: 分別測試:

// fact  fact(10) // Y (f => f(f))(fact => n => n <= 1 ? 1 : n * fact(fact)(n - 1))(10) // Y' const fix = (f) => f(f) const ygen = fix(fact2)  
ygen(10) // callee (function(n) {n<=1?1:n*arguments.callee(n-1)})(10)

環(huán)境:Macbook pro(2.5 GHz Intel Core i7), node-5.0.0(V8:4.6.85.28) 結(jié)果:

fact x 18,604,101 ops/sec ±2.22% (88 runs sampled)

Y x 2,799,791 ops/sec ±1.03% (87 runs sampled)

Y’ x 3,678,654 ops/sec ±1.57% (77 runs sampled)

callee x 2,632,864 ops/sec ±0.99% (81 runs sampled)

可見Y和callee的性能相差不多,因為需要臨時構(gòu)建函數(shù),所以跟直接的fact遞歸調(diào)用有差不多一個數(shù)量級的差異,將不定點函數(shù)算出后保存下來,大概會有一倍左右的性能提升。

 

藍藍設(shè)計m.sdgs6788.com )是一家專注而深入的界面設(shè)計公司,為期望卓越的國內(nèi)外企業(yè)提供卓越的UI界面設(shè)計BS界面設(shè)計 、 cs界面設(shè)計 、 ipad界面設(shè)計 、 包裝設(shè)計 、 圖標(biāo)定制 、 用戶體驗 、交互設(shè)計、 網(wǎng)站建設(shè) 平面設(shè)計服務(wù) 

 

日歷

鏈接

個人資料

藍藍設(shè)計的小編 http://m.sdgs6788.com

存檔

嫩草影院久久99_老司机午夜网站国内精品久久久久久久久_久久夜色精品国产_国产一级做a爰片久久毛片
<em id="09ttv"></em>
    <sup id="09ttv"><pre id="09ttv"></pre></sup>
    <dd id="09ttv"></dd>

        • 亚洲在线一区二区| 国产精品你懂的在线欣赏| 欧美激情精品久久久久久免费印度| 欧美一区二区三区喷汁尤物| 午夜精品视频在线观看一区二区| 亚洲一区二区伦理| 亚洲欧美日韩国产综合精品二区| 午夜精品一区二区三区在线播放 | 久久久久久久尹人综合网亚洲| 欧美在线三区| 蜜桃av一区二区| 欧美日韩日日骚| 国产精品一区二区三区观看| 国产中文一区二区| 在线欧美视频| 在线视频免费在线观看一区二区| 亚洲在线不卡| 麻豆精品国产91久久久久久| 亚洲欧洲日产国产综合网| 亚洲精品一区二区三区婷婷月| 亚洲视频一起| 久久久久久久999| 欧美日韩精品不卡| 国产欧美一区二区色老头| 亚洲黄一区二区三区| 亚洲欧美文学| 亚洲国产合集| 久久精品视频va| 日韩一级裸体免费视频| 先锋影音一区二区三区| 欧美精品粉嫩高潮一区二区 | 亚洲欧美中文在线视频| 免费一级欧美片在线观看| 国产精品毛片在线| 亚洲精品欧美一区二区三区| 欧美一区亚洲一区| 亚洲毛片av| 老司机精品久久| 国产精品视区| 亚洲天堂男人| 亚洲激情视频| 久久亚洲一区二区三区四区| 国产精品久久波多野结衣| 亚洲国内精品| 另类天堂av| 欧美一区二区三区在线| 欧美日韩免费观看一区二区三区| 伊人精品在线| 久久精品日韩欧美| 在线亚洲电影| 欧美四级在线观看| 在线亚洲精品| 亚洲伦理一区| 欧美日韩午夜在线| 99re热这里只有精品免费视频| 免费在线亚洲欧美| 久久久久免费观看| 黄色资源网久久资源365| 久久精品一区二区三区中文字幕| 亚洲图片你懂的| 欧美午夜激情小视频| 在线亚洲免费视频| 99视频在线观看一区三区| 欧美日本精品一区二区三区| 亚洲精品美女在线观看播放| 欧美成人精品高清在线播放| 久久久亚洲综合| 一色屋精品亚洲香蕉网站| 老司机午夜免费精品视频| 欧美在线视频免费| 伊人久久av导航| 欧美大片在线看免费观看| 麻豆精品精华液| 亚洲七七久久综合桃花剧情介绍| 亚洲国产精品一区在线观看不卡 | 国产精品美女久久久久aⅴ国产馆| 一区二区三区回区在观看免费视频| 91久久精品国产91久久性色tv| 欧美国产日韩a欧美在线观看| 日韩亚洲欧美中文三级| 99精品国产高清一区二区| 国产精品国色综合久久| 久久精品国产v日韩v亚洲| 久久亚洲一区二区| 一区二区三欧美| 亚洲欧美激情一区| 在线免费不卡视频| 亚洲美女网站| 国产在线视频不卡二| 亚洲第一二三四五区| 欧美日韩国产在线播放网站| 欧美一区二区视频观看视频| 久久先锋资源| 亚洲综合欧美日韩| 久久精品视频在线| 亚洲深夜av| 久久久噜噜噜| 亚洲一区二区三区在线播放| 欧美一区二区三区四区在线观看 | 亚洲自拍另类| 亚洲成人在线观看视频| 一本久久综合亚洲鲁鲁| 狠狠色噜噜狠狠色综合久| 亚洲人成网站影音先锋播放| 国产日韩欧美日韩大片| 亚洲高清二区| 国产一级一区二区| 夜夜嗨av一区二区三区四区| 一区二区三区在线视频播放| 一区二区三区欧美视频| 亚洲国产欧美日韩另类综合| 亚洲一区不卡| 日韩视频中文字幕| 久久久免费观看视频| 欧美一级二区| 欧美视频一区二区三区…| 欧美大成色www永久网站婷| 国产日韩欧美麻豆| 亚洲深夜av| 亚洲视频在线观看视频| 男女av一区三区二区色多| 久久免费观看视频| 国产欧美视频一区二区| 一本色道久久| 亚洲午夜一二三区视频| 欧美v亚洲v综合ⅴ国产v| 久久久久久欧美| 国产视频一区二区在线观看 | 亚洲激情专区| 亚洲国产精品久久人人爱蜜臀| 欧美一级淫片播放口| 性娇小13――14欧美| 欧美天天综合网| 亚洲精品一二三| 99国产精品国产精品久久| 男人插女人欧美| 欧美高潮视频| 亚洲精品视频在线播放| 欧美成人在线免费观看| 亚洲国产日韩精品| 日韩一区二区免费看| 欧美久久电影| 日韩亚洲在线观看| 亚洲欧美另类久久久精品2019| 欧美三级网页| 亚洲女性裸体视频| 久久精品人人爽| 一区视频在线播放| 蜜臀a∨国产成人精品| 亚洲高清不卡| 亚洲午夜久久久| 国产麻豆91精品| 久久国产主播| 亚洲国产欧洲综合997久久| 一区二区三区高清在线| 国产精品爽爽ⅴa在线观看| 欧美在线免费看| 亚洲电影免费观看高清完整版在线| 亚洲日韩视频| 国产精品久久午夜| 久久久久欧美精品| 日韩写真在线| 久久久久国产成人精品亚洲午夜| 在线精品在线| 欧美视频在线看| 久久久久国产精品www| 最新中文字幕亚洲| 欧美一区三区二区在线观看| 亚洲第一在线| 国产精品劲爆视频| 久久久久国产一区二区三区| 亚洲欧洲一区二区在线播放| 亚洲欧美国产一区二区三区| 狠狠色丁香婷婷综合| 欧美精品午夜| 久久成人精品电影| 亚洲乱码国产乱码精品精| 久久免费偷拍视频| 亚洲少妇最新在线视频| 合欧美一区二区三区| 欧美日韩情趣电影| 久久久久欧美精品| 亚洲永久网站| 亚洲精品在线一区二区| 久久久免费精品视频| 中日韩美女免费视频网址在线观看| 国产一级揄自揄精品视频| 欧美激情中文字幕在线| 欧美在线播放一区二区| 99亚洲精品| 亚洲国产精品高清久久久| 久久精品国产免费看久久精品| 一本色道精品久久一区二区三区 | 日韩视频一区二区在线观看 | 亚洲乱亚洲高清| 麻豆av一区二区三区| 亚洲主播在线| 日韩视频一区二区三区| 影音先锋日韩精品| 国产日韩欧美电影在线观看|