欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript與函數(shù)式編程解釋

 更新時(shí)間:2007年04月27日 00:00:00   作者:  
作者:月影
牢記:函數(shù)式編程不是用函數(shù)來編程!!!
23.4函數(shù)式編程 
23.4.1 什么是函數(shù)式編程

        什么是函數(shù)式編程?如果你這么直白地詢問,會(huì)發(fā)現(xiàn)它竟是一個(gè)不太容易解釋的概念。許多在程序設(shè)計(jì)領(lǐng)域有著多年經(jīng)驗(yàn)的老手,也無法很明白地說清楚函數(shù)式編程到底在研究些什么。函數(shù)式編程對(duì)于熟悉過程式程序設(shè)計(jì)的程序員來說的確是一個(gè)陌生的領(lǐng)域,閉包(closure),延續(xù)(continuation),和柯里化(currying)這些概念看起來是這么的陌生,同我們熟悉的if、else、while沒有任何的相似之處。盡管函數(shù)式編程有著過程式無法比擬的優(yōu)美的數(shù)學(xué)原型,但它又是那么的高深莫測,似乎只有拿著博士學(xué)位的人才玩得轉(zhuǎn)它。

        提示:這一節(jié)有點(diǎn)難,但它并不是掌握J(rèn)avaScript所必需的技能,如果你不想用JavaScript來完成那些用Lisp來完成活兒,或者不想學(xué)函數(shù)式編程這種深?yuàn)W的技巧,你完全可以跳過它們,進(jìn)入下一章的旅程。

        那么回到這個(gè)問題,什么是函數(shù)式編程?答案很長……

函數(shù)式編程第一定律:函數(shù)是第一型。

        這句話本身該如何理解?什么才是真正的第一型?我們看下面的數(shù)學(xué)概念:

        二元方程式 F(x, y) = 0,x, y 是變量, 把它寫成 y = f(x), x是參數(shù),y是返回值,f是由x到y(tǒng)的映射關(guān)系,被稱為函數(shù)。如果又有,G(x, y, z) = 0,或者記為 z = g(x, y),g是x、y到z的映射關(guān)系,也是函數(shù)。如果g的參數(shù)x, y又滿足前面的關(guān)系y = f(x), 那么得到z = g(x, y) = g(x, f(x)),這里有兩重含義,一是f(x)是x上的函數(shù),又是函數(shù)g的參數(shù),二是g是一個(gè)比f更高階的函數(shù)。
        這樣我們就用z = g(x, f(x)) 來表示方程F(x, y) = 0和G(x, y, z) = 0的關(guān)聯(lián)解,它是一個(gè)迭代的函數(shù)。我們也可以用另一種形式來表示g,記z = g(x, y, f),這樣我們將函數(shù)g一般化為一個(gè)高階函數(shù)。同前面相比,后面這種表示方式的好處是,它是一種更加泛化的模型,例如T(x,y) = 0和G(x,y,z) = 0的關(guān)聯(lián)解,我們也可以用同樣的形式來表示(只要令f=t)。在這種支持把問題的解轉(zhuǎn)換成高階函數(shù)迭代的語言體系中,函數(shù)就被稱為“第一型”。
        JavaScript中的函數(shù)顯然是“第一型”。下面就是一個(gè)典型的例子:

        Array.prototype.each = function(closure)
                {
                return this.length ? [closure(this[0])].concat(this.slice(1).each(closure)) : [];
                }

這真是個(gè)神奇的魔法代碼,它充分發(fā)揮了函數(shù)式的魅力,在整個(gè)代碼中只有函數(shù)(function)和符號(hào)(Symbol)。它形式簡潔并且威力無窮。
[1,2,3,4].each(function(x){return x * 2})得到[2,4,6,8],而[1,2,3,4].each(function(x){return x-1})得到[0,1,2,3]。

函數(shù)式和面向?qū)ο蟮谋举|(zhì)都是“道法自然”。如果說,面向?qū)ο笫且环N真實(shí)世界的模擬的話,那么函數(shù)式就是數(shù)學(xué)世界的模擬,從某種意義上說,它的抽象程度比面向?qū)ο蟾?,因?yàn)閿?shù)學(xué)系統(tǒng)本來就具有自然界所無法比擬的抽象性。

函數(shù)式編程第二定律:閉包是函數(shù)式編程的摯友。

閉包,在前面的章節(jié)中我們已經(jīng)解釋過了,它對(duì)于函數(shù)式編程非常重要。它最大的特點(diǎn)是不需要通過傳遞變量(符號(hào))的方式就可以從內(nèi)層直接訪問外層的環(huán)境,這為多重嵌套下的函數(shù)式程序帶來了極大的便利性,下面是一個(gè)例子:

(function outerFun(x)
{
        return function innerFun(y)
        {
                return x * y;
        }
})(2)(3);

函數(shù)式編程第三定律:函數(shù)可以被科里化(Currying)。

什么是Currying? 它是一個(gè)有趣的概念。還是從數(shù)學(xué)開始:我們說,考慮一個(gè)三維空間方程 F(x, y, z) = 0,如果我們限定z = 0,于是得到 F(x, y, 0) = 0 記為 F'(x, y)。這里F'顯然是一個(gè)新的方程式,它代表三維空間曲線F(x, y, z)在z = 0平面上的兩維投影。記y = f(x, z), 令z = 0, 得到 y = f(x, 0),記為 y = f'(x), 我們說函數(shù)f'是f的一個(gè)Currying解。
下面給出了JavaScript的Currying的例子:
function add(x, y)
{
        if(x!=null && y!=null) return x + y;
                else if(x!=null && y==null) return function(y)
                {
                return x + y;
                }
                else if(x==null && y!=null) return function(x)
                {
                       return x + y;
                 }
}
var a = add(3, 4);
var b = add(2);
var c = b(10);

上面的例子中,b=add(2)得到的是一個(gè)add()的Currying函數(shù),它是當(dāng)x = 2時(shí),關(guān)于參數(shù)y的函數(shù),注意到上面也用到了閉包的特性。

有趣的是,我們可以給任意函數(shù)一般化Currying,例如:

function Foo(x, y, z, w)
{
        var args = arguments;

        if(Foo.length < args.length)
                return function()
                {
                        return 
args.callee.apply(Array.apply([], args).concat(Array.apply([], arguments)));
                }
        else
                return x + y – z * w;
}

函數(shù)式編程第四定律:延遲求值和延續(xù)。
        //TODO:這里再考慮下


23.4.2 函數(shù)式編程的優(yōu)點(diǎn)

單元測試

嚴(yán)格函數(shù)式編程的每一個(gè)符號(hào)都是對(duì)直接量或者表達(dá)式結(jié)果的引用,沒有函數(shù)產(chǎn)生副作用。因?yàn)閺奈丛谀硞€(gè)地方修改過值,也沒有函數(shù)修改過在其作用域之外的量并被其他函數(shù)使用(如類成員或全局變量)。這意味著函數(shù)求值的結(jié)果只是其返回值,而惟一影響其返回值的就是函數(shù)的參數(shù)。
這是單元測試者的夢(mèng)中仙境(wet dream)。對(duì)被測試程序中的每個(gè)函數(shù),你只需在意其參數(shù),而不必考慮函數(shù)調(diào)用順序,不用謹(jǐn)慎地設(shè)置外部狀態(tài)。所有要做的就是傳遞代表了邊際情況的參數(shù)。如果程序中的每個(gè)函數(shù)都通過了單元測試,你就對(duì)這個(gè)軟件的質(zhì)量有了相當(dāng)?shù)淖孕?。而命令式編程就不能這樣樂觀了,在 Java 或 C++ 中只檢查函數(shù)的返回值還不夠——我們還必須驗(yàn)證這個(gè)函數(shù)可能修改了的外部狀態(tài)。

調(diào)試

如果一個(gè)函數(shù)式程序不如你期望地運(yùn)行,調(diào)試也是輕而易舉。因?yàn)楹瘮?shù)式程序的 bug 不依賴于執(zhí)行前與其無關(guān)的代碼路徑,你遇到的問題就總是可以再現(xiàn)。在命令式程序中,bug 時(shí)隱時(shí)現(xiàn),因?yàn)樵谀抢锖瘮?shù)的功能依賴與其他函數(shù)的副作用,你可能會(huì)在和 bug 的產(chǎn)生無關(guān)的方向探尋很久,毫無收獲。函數(shù)式程序就不是這樣——如果一個(gè)函數(shù)的結(jié)果是錯(cuò)誤的,那么無論之前你還執(zhí)行過什么,這個(gè)函數(shù)總是返回相同的錯(cuò)誤結(jié)果。
一旦你將那個(gè)問題再現(xiàn)出來,尋其根源將毫不費(fèi)力,甚至?xí)屇汩_心。中斷那個(gè)程序的執(zhí)行然后檢查堆棧,和命令式編程一樣,棧里每一次函數(shù)調(diào)用的參數(shù)都呈現(xiàn)在你眼前。但是在命令式程序中只有這些參數(shù)還不夠,函數(shù)還依賴于成員變量,全局變量和類的狀態(tài)(這反過來也依賴著這許多情況)。函數(shù)式程序里函數(shù)只依賴于它的參數(shù),而那些信息就在你注視的目光下!還有,在命令式程序里,只檢查一個(gè)函數(shù)的返回值不能夠讓你確信這個(gè)函數(shù)已經(jīng)正常工作了,你還要去查看那個(gè)函數(shù)作用域外數(shù)十個(gè)對(duì)象的狀態(tài)來確認(rèn)。對(duì)函數(shù)式程序,你要做的所有事就是查看其返回值!
沿著堆棧檢查函數(shù)的參數(shù)和返回值,只要發(fā)現(xiàn)一個(gè)不盡合理的結(jié)果就進(jìn)入那個(gè)函數(shù)然后一步步跟蹤下去,重復(fù)這一個(gè)過程,直到它讓你發(fā)現(xiàn)了 bug 的生成點(diǎn)。

并行
函數(shù)式程序無需任何修改即可并行執(zhí)行。不用擔(dān)心死鎖和臨界區(qū),因?yàn)槟銖奈从面i!函數(shù)式程序里沒有任何數(shù)據(jù)被同一線程修改兩次,更不用說兩個(gè)不同的線程了。這意味著可以不假思索地簡單增加線程而不會(huì)引發(fā)折磨著并行應(yīng)用程序的傳統(tǒng)問題。
事實(shí)既然如此,為什么并不是所有人都在需要高度并行作業(yè)的應(yīng)用中采用函數(shù)式程序?嗯,他們正在這樣做。愛立信公司設(shè)計(jì)了一種叫作 Erlang 的函數(shù)式語言并將它使用在需要極高抗錯(cuò)性和可擴(kuò)展性的電信交換機(jī)上。還有很多人也發(fā)現(xiàn)了 Erlang 的優(yōu)勢(shì)并開始使用它。我們談?wù)摰氖请娦磐ㄐ趴刂葡到y(tǒng),這與設(shè)計(jì)華爾街的典型系統(tǒng)相比對(duì)可靠性和可升級(jí)性要求高了得多。實(shí)際上,Erlang 系統(tǒng)并不可靠和易擴(kuò)展,JavaScript 才是。Erlang 系統(tǒng)只是堅(jiān)如磐石。
關(guān)于并行的故事還沒有就此停止,即使你的程序本身就是單線程的,那么函數(shù)式程序的編譯器仍然可以優(yōu)化它使其運(yùn)行于多個(gè)CPU上。請(qǐng)看下面這段代碼:

String s1 = somewhatLongOperation1();
String s2 = somewhatLongOperation2();
String s3 = concatenate(s1, s2);

在函數(shù)編程語言中,編譯器會(huì)分析代碼,辨認(rèn)出潛在耗時(shí)的創(chuàng)建字符串s1和s2的函數(shù),然后并行地運(yùn)行它們。這在命令式語言中是不可能的,因?yàn)樵谀抢?,每個(gè)函數(shù)都有可能修改了函數(shù)作用域以外的狀態(tài)并且其后續(xù)的函數(shù)又會(huì)依賴這些修改。在函數(shù)式語言里,自動(dòng)分析函數(shù)并找出適合并行執(zhí)行的候選函數(shù)簡單的像自動(dòng)進(jìn)行的函數(shù)內(nèi)聯(lián)化!在這個(gè)意義上,函數(shù)式風(fēng)格的程序是“不會(huì)過時(shí)的技術(shù)(future proof)”(即使不喜歡用行業(yè)術(shù)語,但這回要破例一次)。硬件廠商已經(jīng)無法讓CPU運(yùn)行得更快了,于是他們?cè)黾恿颂幚砥骱诵牡乃俣炔⒁虿⑿卸@得了四倍的速度提升。當(dāng)然他們也順便忘記提及我們的多花的錢只是用在了解決平行問題的軟件上了。一小部分的命令式軟件和 100% 的函數(shù)式軟件都可以直接并行運(yùn)行于這些機(jī)器上。

代碼熱部署

過去要在 Windows上安裝更新,重啟計(jì)算機(jī)是難免的,而且還不只一次,即使是安裝了一個(gè)新版的媒體播放器。Windows XP 大大改進(jìn)了這一狀態(tài),但仍不理想(我今天工作時(shí)運(yùn)行了Windows Update,現(xiàn)在一個(gè)煩人的圖標(biāo)總是顯示在托盤里除非我重啟一次機(jī)器)。Unix系統(tǒng)一直以來以更好的模式運(yùn)行,安裝更新時(shí)只需停止系統(tǒng)相關(guān)的組件,而不是整個(gè)操作系統(tǒng)。即使如此,對(duì)一個(gè)大規(guī)模的服務(wù)器應(yīng)用這還是不能令人滿意的。電信系統(tǒng)必須100%的時(shí)間運(yùn)行,因?yàn)槿绻谙到y(tǒng)更新時(shí)緊急撥號(hào)失效,就可能造成生命的損失。華爾街的公司也沒有理由必須在周末停止服務(wù)以安裝更新。
理想的情況是完全不停止系統(tǒng)任何組件來更新相關(guān)的代碼。在命令式的世界里這是不可能的??紤]運(yùn)行時(shí)上載一個(gè)Java類并重載一個(gè)新的定義,那么所有這個(gè)類的實(shí)例都將不可用,因?yàn)樗鼈儽槐4娴臓顟B(tài)丟失了。我們可以著手寫些繁瑣的版本控制代碼來解決這個(gè)問題,然后將這個(gè)類的所有實(shí)例序列化,再銷毀這些實(shí)例,繼而用這個(gè)類新的定義來重新創(chuàng)建這些實(shí)例,然后載入先前被序列化的數(shù)據(jù)并希望載入代碼可以恰到地將這些數(shù)據(jù)移植到新的實(shí)例。在此之上,每次更新都要重新手動(dòng)編寫這些用來移植的代碼,而且要相當(dāng)謹(jǐn)慎地防止破壞對(duì)象間的相互關(guān)系。理論簡單,但實(shí)踐可不容易。
對(duì)函數(shù)式的程序,所有的狀態(tài)即傳遞給函數(shù)的參數(shù)都被保存在了堆棧上,這使的熱部署輕而易舉!實(shí)際上,所有我們需要做的就是對(duì)工作中的代碼和新版本的代碼做一個(gè)差異比較,然后部署新代碼。其他的工作將由一個(gè)語言工具自動(dòng)完成!如果你認(rèn)為這是個(gè)科幻故事,請(qǐng)?jiān)偎伎家幌隆6嗄陙?nbsp;Erlang工程師一直更新著他們的運(yùn)轉(zhuǎn)著的系統(tǒng),而無需中斷它。

機(jī)器輔助的推理和優(yōu)化

函數(shù)式語言的一個(gè)有趣的屬性就是他們可以用數(shù)學(xué)方式推理。因?yàn)橐环N函數(shù)式語言只是一個(gè)形式系統(tǒng)的實(shí)現(xiàn),所有在紙上完成的運(yùn)算都可以應(yīng)用于用這種語言書寫的程序。編譯器可以用數(shù)學(xué)理論將轉(zhuǎn)換一段代碼轉(zhuǎn)換為等價(jià)的但卻更高效的代碼[7]。多年來關(guān)系數(shù)據(jù)庫一直在進(jìn)行著這類優(yōu)化。沒有理由不能把這一技術(shù)應(yīng)用到常規(guī)軟件上。
另外,還能使用這些技術(shù)來證明部分程序的正確,甚至可能創(chuàng)建工具來分析代碼并為單元測試自動(dòng)生成邊界用例!對(duì)穩(wěn)固的系統(tǒng)這種功能沒有價(jià)值,但如果你要設(shè)計(jì)心房脈沖產(chǎn)生器 (pace maker)或空中交通控制系統(tǒng),這種工具就不可或缺。如果你編寫的應(yīng)用程序不是產(chǎn)業(yè)的核心任務(wù),這類工具也是你強(qiáng)于競爭對(duì)手的殺手锏。

23.4.3 函數(shù)式編程的缺點(diǎn)

閉包的副作用

        非嚴(yán)格函數(shù)式編程中,閉包可以改寫外部環(huán)境(在上一章中我們已經(jīng)見過了),這帶來了副作用,當(dāng)這種副作用頻繁出現(xiàn)并經(jīng)常改變程序運(yùn)行環(huán)境時(shí),錯(cuò)誤就變得難以跟蹤。
        //TODO:

遞歸的形式

        盡管遞歸通常是一種最簡潔的表達(dá)形式,但它確實(shí)不如非遞歸的循環(huán)來的直觀。
        //TODO:

延遲取值的弱點(diǎn)

        //TODO:

相關(guān)文章

  • js實(shí)現(xiàn)圖片粘貼上傳到服務(wù)器并展示的實(shí)例

    js實(shí)現(xiàn)圖片粘貼上傳到服務(wù)器并展示的實(shí)例

    下面小編就為大家?guī)硪黄猨s實(shí)現(xiàn)圖片粘貼上傳到服務(wù)器并展示的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-11-11
  • JavaScript async/await原理及實(shí)例解析

    JavaScript async/await原理及實(shí)例解析

    這篇文章主要介紹了JavaScript async/await原理及實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • JavaScript判斷對(duì)象是否為空對(duì)象的幾種常見方法

    JavaScript判斷對(duì)象是否為空對(duì)象的幾種常見方法

    經(jīng)典面試題,研發(fā)時(shí)也經(jīng)常遇見的一個(gè)問題,如何判斷一個(gè)對(duì)象是空對(duì)象,這篇文章主要給大家介紹了關(guān)于JavaScript判斷對(duì)象是否為空對(duì)象的幾種常見方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • 微信小程序?qū)崿F(xiàn)的一鍵連接wifi功能示例

    微信小程序?qū)崿F(xiàn)的一鍵連接wifi功能示例

    這篇文章主要介紹了微信小程序?qū)崿F(xiàn)的一鍵連接wifi功能,結(jié)合實(shí)例形式分析了微信小程序操作WiFi連接的模塊初始化、配置、連接等相關(guān)操作技巧,需要的朋友可以參考下
    2019-04-04
  • 微信小程序手機(jī)號(hào)碼驗(yàn)證功能的實(shí)例代碼

    微信小程序手機(jī)號(hào)碼驗(yàn)證功能的實(shí)例代碼

    這篇文章主要介紹了微信小程序手機(jī)號(hào)碼驗(yàn)證功能的實(shí)例代碼及微信小程序正則判斷手機(jī)號(hào)的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • 分享一個(gè)自己寫的table表格排序js插件(高效簡潔)

    分享一個(gè)自己寫的table表格排序js插件(高效簡潔)

    在前不久做的一個(gè)web項(xiàng)目中,需要實(shí)現(xiàn)js表格排序的效果,當(dāng)時(shí)為了省事,就在網(wǎng)上找了幾個(gè)相關(guān)的js插件
    2011-10-10
  • JavaScript自執(zhí)行閉包的小例子

    JavaScript自執(zhí)行閉包的小例子

    JQuery是一個(gè)很明顯的自執(zhí)行閉包的例子,有需要的朋友可以參考一下
    2013-06-06
  • JavaScript 用fetch 實(shí)現(xiàn)異步下載文件功能

    JavaScript 用fetch 實(shí)現(xiàn)異步下載文件功能

    本文通過實(shí)例代碼給大家介紹了JavaScript 用fetch 實(shí)現(xiàn)異步下載文件功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-07-07
  • 淺析JavaScript 調(diào)試方法和技巧

    淺析JavaScript 調(diào)試方法和技巧

    本文給大家淺析javascript調(diào)試方法和技巧,涉及到j(luò)avascript調(diào)試方法和技巧方面的知識(shí),本文介紹的非常詳細(xì),非常具有參考借鑒介紹,感興趣的朋友一起看看吧
    2015-10-10
  • 如何用JS判斷數(shù)組中是否存在某個(gè)值或者某個(gè)對(duì)象的值

    如何用JS判斷數(shù)組中是否存在某個(gè)值或者某個(gè)對(duì)象的值

    數(shù)組是我們編程中經(jīng)常使用的的數(shù)據(jù)結(jié)構(gòu)之一,在處理數(shù)組時(shí),我們經(jīng)常需要在數(shù)組中查找特定的值,下面這篇文章主要給大家介紹了關(guān)于如何用JS判斷數(shù)組中是否存在某個(gè)值或者某個(gè)對(duì)象的值的相關(guān)資料,需要的朋友可以參考下
    2023-01-01

最新評(píng)論