亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

學(xué)會(huì)使用函數(shù)式編程的程序員(第1部分)

Steven / 424人閱讀

摘要:函數(shù)式編程的目標(biāo)是盡量寫更多的純函數(shù),并將其與程序的其他部分隔離開來。在函數(shù)式編程中,是非法的。函數(shù)式編程使用參數(shù)保存狀態(tài),最好的例子就是遞歸。函數(shù)式編程使用遞歸進(jìn)行循環(huán)。在函數(shù)式編程中,函數(shù)是一級(jí)公民。

想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!

在這篇由多部分組成的文章中,接下來將介紹函數(shù)式編程的一些概念,這些概念對(duì)你學(xué)習(xí)函數(shù)式編程有所幫助。如果你已經(jīng)懂了什么是函數(shù)式編程,這可以加深你的理解。

請(qǐng)不要著急。從這一點(diǎn)開始,花點(diǎn)時(shí)間閱讀并理解代碼示例。你甚至可能想在每節(jié)課結(jié)束后停止閱讀,以便讓你的觀點(diǎn)深入理解,然后再回來完成。

最重要的是你要理解。

純函數(shù)(Purity)

所謂純函數(shù),就是指這樣一個(gè)函數(shù),對(duì)于相同的輸入,永遠(yuǎn)得到相同的輸出,它不依賴外部環(huán)境,也不會(huì)改變外部環(huán)境。如果不滿足以上幾個(gè)條件那就是非純函數(shù)。

下面是Javascript中的一個(gè)純函數(shù)示例:

var z = 10;
function add(x, y) {
    return x + y;
}

注意,add 函數(shù)不涉及z變量。它不從z讀取,也不從z寫入,它只讀取xy,然后返回它們相加的結(jié)果。這是一個(gè)純函數(shù)。如果 add 函數(shù)確實(shí)訪問了變量z,那么它就不再是純函數(shù)了。

請(qǐng)思考一下下面這個(gè)函數(shù):

function justTen() {
    return 10;
}

如果函數(shù)justTen是純的,那么它只能返回一個(gè)常量, 為什么?

因?yàn)槲覀儧]有給它任何參數(shù)。 而且,既然是純函數(shù)的,除了自己的輸入之外它不能訪問任何東西,它唯一可以返回的就是常量。

由于不帶參數(shù)的純函數(shù)不起作用,所以它們不是很有用。所以justTen被定義為一個(gè)常數(shù)會(huì)更好。

大多數(shù)有用的純函數(shù)必須至少帶一個(gè)參數(shù)。

考慮一下這個(gè)函數(shù):

function addNoReturn(x, y) {
    var z = x + y
}

注意這個(gè)函數(shù)是不返回任何值。它只是把變量xy相加賦給變量z,但并沒有返回。

這個(gè)也是一個(gè)純函數(shù),因?yàn)樗惶幚磔斎?。它確實(shí)對(duì)輸入的變量進(jìn)行操作,但是由于它不返回結(jié)果,所以它是無用的。

所有有用的純函數(shù)都必須返回一些我們期望的結(jié)果。

讓我們?cè)俅慰紤]第一個(gè)add函數(shù):

注意 add(1, 2) 的返回結(jié)果總是 3。這不是奇怪的事情,只是因?yàn)?add 函數(shù)是純的。如果 add 函數(shù)使用了一些外部值,那么你永遠(yuǎn)無法預(yù)測(cè)它的行為。

在給定相同輸入的情況下,純函數(shù)總是返回相同的結(jié)果。

由于純函數(shù)不能改變?nèi)魏瓮獠孔兞浚韵旅娴暮瘮?shù)都不是純函數(shù):

writeFile(fileName);
updateDatabaseTable(sqlCmd);            
sendAjaxRequest(ajaxRequest);
openSocket(ipAddress);

所有這些功能都有副作用。當(dāng)你調(diào)用它們時(shí),它們會(huì)更改文件和數(shù)據(jù)庫表、將數(shù)據(jù)發(fā)送到服務(wù)器或調(diào)用操作系統(tǒng)以獲取套接字。它們不僅對(duì)輸入操作同時(shí)也對(duì)輸出進(jìn)行操作,因此,你永遠(yuǎn)無法預(yù)測(cè)這些函數(shù)將返回什么。

純函數(shù)沒有副作用。

在Javascript、Java 和 c# 等命令式編程語言中,副作用無處不在。這使得調(diào)試非常困難,因?yàn)樽兞靠梢栽诔绦虻娜魏蔚胤礁?。所以,?dāng)你有一個(gè)錯(cuò)誤,因?yàn)橐粋€(gè)變量在錯(cuò)誤的時(shí)間被更改為錯(cuò)誤的值,這不是很好。

此時(shí),你可能會(huì)想,“我怎么可能只使用純函數(shù)呢?”

函數(shù)式編程不能消除副作用,只能限制副作用。由于程序必須與真實(shí)環(huán)境相連接,所以每個(gè)程序的某些部分肯定是不純的。函數(shù)式編程的目標(biāo)是盡量寫更多的純函數(shù),并將其與程序的其他部分隔離開來。

不可變性 (Immutability)

你還記得你第一次看到下面的代碼是什么時(shí)候嗎?

var x = 1;
x = x + 1;

教你初中數(shù)學(xué)的老師看到以上代碼,可能會(huì)問你,你忘記我給你教的數(shù)學(xué)了嗎? 因?yàn)樵跀?shù)學(xué)中,x 永遠(yuǎn)不能等于x + 1。

但在命令式編程中,它的意思是,取x的當(dāng)前值加1,然后把結(jié)果放回x中。

在函數(shù)式編程中,x = x + 1是非法的。所以這里你可以用數(shù)學(xué)的邏輯還記得在數(shù)式編程中這樣寫是不對(duì)的!

函數(shù)式編程中沒有變量。

由于歷史原因,存儲(chǔ)值的變量仍然被稱為變量,但它們是常量,也就是說,一旦x取值,這個(gè)常量就是x返回的值。別擔(dān)心,x 通常是一個(gè)局部變量,所以它的生命周期通常很短。但只要它還沒被銷毀,它的值就永遠(yuǎn)不會(huì)改變。

下面是Elm中的常量變量示例,Elm是一種用于Web開發(fā)的純函數(shù)式編程語言:

addOneToSum y z =
    let
        x = 1
    in
        x + y + z

如果你不熟悉ml風(fēng)格的語法,讓我解釋一下。addOneToSum 是一個(gè)函數(shù),有兩個(gè)參數(shù)分別為yz。

let塊中,x被綁定到1的值上,也就是說,它在函數(shù)的生命周期內(nèi)都等于1。當(dāng)函數(shù)退出時(shí),它的生命周期結(jié)束,或者更準(zhǔn)確地說,當(dāng)let塊被求值時(shí),它的生命周期就結(jié)束了。

in塊中,計(jì)算可以包含在let塊中定義的值,即 x,返回計(jì)算結(jié)果 x + y + z,或者更準(zhǔn)確地說,返回 1 + y + z,因?yàn)?x = 1。

你可能又會(huì)想 :“我怎么能在沒有變量的情況下做任何事情呢?”

我們想一下什么時(shí)候需要修改變量。通常會(huì)想到兩種情況:多值更改(例如修改或記錄對(duì)象中的單個(gè)值)和單值更改(例如循環(huán)計(jì)數(shù)器)。

函數(shù)式編程使用參數(shù)保存狀態(tài),最好的例子就是遞歸。是的,是沒有循環(huán)?!笆裁礇]有變量,現(xiàn)在又沒有循環(huán)? ”我討厭你! ! !”

哈哈,這并不是說我們不能做循環(huán),只是沒有特定的循環(huán)結(jié)構(gòu),比如for, while, do, repeat等等。

函數(shù)式編程使用遞歸進(jìn)行循環(huán)。

這里有兩種方法可以在Javascript中執(zhí)行循環(huán):

注意,遞歸是一種函數(shù)式方法,它通過使用一個(gè)結(jié)束條件 start (start + 1) 和調(diào)用自己 accumulator (acc + start) 來實(shí)現(xiàn)與 for 循環(huán)相同的功能。它不會(huì)修改舊的值。相反,它使用從舊值計(jì)算的新值。

不幸的是,這在 Javascript中 很難想懂,需要你花點(diǎn)時(shí)間研究它,原因有二。第一,Javascript的語法相對(duì)其它高級(jí)語言比較亂,其次,你可能還不習(xí)慣遞歸思維。

在Elm,它更容易閱讀,如下:

sumRange start end acc =
    if start > end then
        acc
    else
        sumRange (start + 1) end (acc + start) 
    

它是這樣運(yùn)行的:

你可能認(rèn)為 for 循環(huán)更容易理解。雖然這是有爭(zhēng)議的,而且更可能是一個(gè)熟悉的問題,但非遞歸循環(huán)需要可變性,這是不好的。

在這里,我還沒有完全解釋不變性的好處,但是請(qǐng)查看全局可變狀態(tài)部分,即為什么程序員需要限制來了解更多。

我還沒有完全解釋不可變性(Immutability)在這里的好處,但請(qǐng)查看 為什么程序員需要限制的全局可變狀態(tài)部分 以了解更多信息。

不可變性的好處是,你讀取訪問程序中的某個(gè)值,但只有讀權(quán)限的,這意味著不用害怕其他人更改該值使自己讀取到的值是錯(cuò)誤。

不可變性的還有一個(gè)好處是,如果你的程序是多線程的,那么就沒有其他線程可以更改你線程中的值,因?yàn)樵撝凳遣豢勺?,所以另一個(gè)線程想要更改它,它只能從舊線程創(chuàng)建一個(gè)新值。

不變性可以創(chuàng)建更簡(jiǎn)單、更安全的代碼。
重構(gòu)

讓我們考慮一下重構(gòu),下面是一些Javascript代碼:

我們以前可能都寫過這樣的代碼,隨著時(shí)間的推移,開始意識(shí)到這兩個(gè)函數(shù)實(shí)際上是相同的,函數(shù)名稱,打印結(jié)果不太一樣而已。

我們不應(yīng)該復(fù)制 validateSsn 來創(chuàng)建 validatePhone,而是應(yīng)該創(chuàng)建一個(gè)函數(shù)(共同的部分),通過參數(shù)形式實(shí)現(xiàn)我們想要的結(jié)果。

重構(gòu)后的代碼如下:

舊代碼參數(shù)中 ssnphone 現(xiàn)在用 value 表示,正則表達(dá)式 /^d{3}-d{2}-d{4}$/ and /^(d{3})d{3}-d{4}$/ 由變量 regex. 表示。最后,消息“SSN”“電話號(hào)碼” 由變量 type 表示。

這個(gè)有類似的函數(shù)都可以使用這個(gè)函數(shù)來實(shí)現(xiàn),這樣可以保持代碼的整潔和可維護(hù)性。

高階函數(shù)

許多語言不支持將函數(shù)作為參數(shù)傳遞,有些會(huì)支持但并不容易。

在函數(shù)式編程中,函數(shù)是一級(jí)公民。換句話說,函數(shù)通常是另一個(gè)函數(shù)的值。

由于函數(shù)只是值,我們可以將它們作為參數(shù)傳遞。即使Javascript不是純函數(shù)語言,也可以使用它進(jìn)行一些功能性的操作。 所以這里將上面的兩個(gè)函數(shù)重構(gòu)為單個(gè)函數(shù),方法是將驗(yàn)證合法性的函數(shù)作為函數(shù) parseFunc 的參數(shù):

function validateValueWithFunc(value, parseFunc, type) {
  if (parseFunc(value))
    console.log("Invalid " + type);
  else
    console.log("Valid " + type);
}

像函數(shù) parseFunc 接收一個(gè)或多個(gè)函數(shù)作為輸入的函數(shù),稱為 高階函數(shù)。

高階函數(shù)要么接受函數(shù)作為參數(shù),要么返回函數(shù),要么兩者兼而有之。

現(xiàn)在可以調(diào)用高階函數(shù)(這在Javascript中有效,因?yàn)镽egex.exec在找到匹配時(shí)返回一個(gè)truthy值):

validateValueWithFunc("123-45-6789", /^d{3}-d{2}-d{4}$/.exec, "SSN");
validateValueWithFunc("(123)456-7890", /^(d{3})d{3}-d{4}$/.exec, "Phone");
validateValueWithFunc("123 Main St.", parseAddress, "Address");
validateValueWithFunc("Joe Mama", parseName, "Name");

這比有四個(gè)幾乎相同的函數(shù)要好得多。

但是請(qǐng)注意正則表達(dá)式,這里有點(diǎn)冗長(zhǎng)了。簡(jiǎn)化一下:

var parseSsn = /^d{3}-d{2}-d{4}$/.exec;
var parsePhone = /^(d{3})d{3}-d{4}$/.exec;
validateValueWithFunc("123-45-6789", parseSsn, "SSN");
validateValueWithFunc("(123)456-7890", parsePhone, "Phone");
validateValueWithFunc("123 Main St.", parseAddress, "Address");
validateValueWithFunc("Joe Mama", parseName, "Name");

現(xiàn)在看起來好多了。現(xiàn)在,當(dāng)要驗(yàn)證一個(gè)電話號(hào)碼時(shí),不需要復(fù)制和粘貼正則表達(dá)式了。

但是假設(shè)我們有更多的正則表達(dá)式需要解析,而不僅僅是 parseSsnparsePhone。每次創(chuàng)建正則表達(dá)式解析器時(shí),我們都必須記住在末尾添加 .exec,這很容易被忘記。

可以通過創(chuàng)建一個(gè)返回exec 的高階函數(shù)來防止這種情況:

function makeRegexParser(regex) {
    return regex.exec;
}
var parseSsn = makeRegexParser(/^d{3}-d{2}-d{4}$/);
var parsePhone = makeRegexParser(/^(d{3})d{3}-d{4}$/);
validateValueWithFunc("123-45-6789", parseSsn, "SSN");
validateValueWithFunc("(123)456-7890", parsePhone, "Phone");
validateValueWithFunc("123 Main St.", parseAddress, "Address");
validateValueWithFunc("Joe Mama", parseName, "Name");

這里,makeRegexParser采用正則表達(dá)式并返回exec函數(shù),該函數(shù)接受一個(gè)字符串。validateValueWithFunc 將字符串 value 傳遞給 parse 函數(shù),即exec。

parseSsnparsePhone 實(shí)際上與以前一樣,是正則表達(dá)式的 exec 函數(shù)。

當(dāng)然,這是一個(gè)微小的改進(jìn),但是這里給出了一個(gè)返回函數(shù)的高階函數(shù)示例。但是,如果makeRegexParser 要復(fù)雜得多,這種更改的好處是很大的。

下面是另一個(gè)返回函數(shù)的高階函數(shù)示例:

function makeAdder(constantValue) {
    return function adder(value) {
        return constantValue + value;
    };
}

函數(shù) makeAdder,接受參數(shù) constantValue 并返回函數(shù) adder,這個(gè)函數(shù)返回 constantValue 與它傳入?yún)?shù)相加結(jié)果。

下面是它的用法:

var add10 = makeAdder(10);
console.log(add10(20)); // 打印 30
console.log(add10(30)); // 打印 40
console.log(add10(40)); // 打印 50

我們通過將常量10傳遞給 makeAdder 來創(chuàng)建一個(gè)函數(shù) add10, makeAdder 返回一個(gè)函數(shù),該函數(shù)將向返回的結(jié)果都加 10。

注意,即使在 makeAddr 返回之后,函數(shù) adder 也可以訪問變量 constantValue。 這里能訪問到 constantValue 是因?yàn)榇嬖陂]包。

閉包機(jī)制非常重要,因?yàn)槿绻麤]有它 ,返回函數(shù)的函數(shù)就不會(huì)有很大作用。所以必須了解它們是如何工作。

閉包

下面是一個(gè)使用閉包的函數(shù)的示例:

function grandParent(g1, g2) {
    var g3 = 3;
    return function parent(p1, p2) {
        var p3 = 33;
        return function child(c1, c2) {
            var c3 = 333;
            return g1 + g2 + g3 + p1 + p2 + p3 + c1 + c2 + c3;
        };
    };
}

在這個(gè)例子中,child 函數(shù)可以訪問它自身的變量,函數(shù) parent 函數(shù)可以訪問它的自身變量和函數(shù) grandParent 的變量。而函數(shù) grandParent 只能訪問自身的變量。

下面是它的一個(gè)使用例子:

var parentFunc = grandParent(1, 2); // returns parent()
var childFunc = parentFunc(11, 22); // returns child()
console.log(childFunc(111, 222)); // prints 738
// 1 + 2 + 3 + 11 + 22 + 33 + 111 + 222 + 333 == 738

在這里,parentFunc 保留了 parent 的作用域,因?yàn)?grandParent 返回 parent。

類似地,childFunc 保留了 child 的作用域,因?yàn)?parentFunc 保留了 parent 的作用域,而 parent 的作用域 保留了 child 的作用域。

當(dāng)一個(gè)函數(shù)被創(chuàng)建時(shí),它在創(chuàng)建時(shí)作用域中的所有變量在函數(shù)的生命周期內(nèi)都是可訪問的。一個(gè)函數(shù)只要還有對(duì)它的引用就存在。例如,只要childFunc 還引用 child 的作用域,child 的作用域就存在。

閉包具體還看看之前整理的一篇文章:我從來不理解JavaScript閉包,直到有人這樣向我解釋它...

原文:
1、https://medium.com/@cscalfani...
2、https://medium.com/@cscalfani...

編輯中可能存在的bug沒法實(shí)時(shí)知道,事后為了解決這些bug,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具Fundebug。

你的點(diǎn)贊是我持續(xù)分享好東西的動(dòng)力,歡迎點(diǎn)贊!

交流

干貨系列文章匯總?cè)缦?,覺得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。

https://github.com/qq44924588...

我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!

關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/100415.html

相關(guān)文章

  • 學(xué)會(huì)使用函數(shù)編程序員(2部分)

    摘要:想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳博客一年百來篇優(yōu)質(zhì)文章等著你本系列的第一篇學(xué)會(huì)使用函數(shù)式編程的程序員第部分組合函數(shù)作為程序員,我們是懶惰的??吕锘址Q部分求值。一旦使用函數(shù)式語言,任何東西都是不可變的。在函數(shù)式語言中,這個(gè)函數(shù)稱為。 showImg(https://segmentfault.com/img/bVblEzw?w=800&h=355); 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一...

    Hwg 評(píng)論0 收藏0
  • 學(xué)會(huì)使用函數(shù)編程序員(3部分)

    摘要:即使你有一個(gè)多線程程序,大多數(shù)線程都被阻塞等待完成,例如文件,網(wǎng)絡(luò)等等。但是只要能夠提升我們程序的效率,要付出努力來寫好多線程程序這是值得的。然而,多線程有兩個(gè)主要問題多線程程序難于編寫讀取解釋測(cè)試和調(diào)試。 showImg(https://segmentfault.com/img/bVblEzw?w=800&h=355); 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等...

    scwang90 評(píng)論0 收藏0
  • javascript 函數(shù)編程思想

    摘要:今天這篇文章主要介紹函數(shù)式編程的思想。函數(shù)式編程通過最小化變化使得代碼更易理解。在函數(shù)式編程里面,組合是一個(gè)非常非常非常重要的思想。可以看到函數(shù)式編程在開發(fā)中具有聲明模式。而函數(shù)式編程旨在盡可能的提高代碼的無狀態(tài)性和不變性。 最開始接觸函數(shù)式編程的時(shí)候是在小米工作的時(shí)候,那個(gè)時(shí)候看老大以前寫的代碼各種 compose,然后一些 ramda 的一些工具函數(shù),看著很吃力,然后極力吐槽函數(shù)式...

    YPHP 評(píng)論0 收藏0
  • 翻譯連載 |《你不知道JS》姊妹篇 |《JavaScript 輕量級(jí)函數(shù)編程》- 1 章:

    摘要:所以我覺得函數(shù)式編程領(lǐng)域更像學(xué)者的領(lǐng)域。函數(shù)式編程的原則是完善的,經(jīng)過了深入的研究和審查,并且可以被驗(yàn)證。函數(shù)式編程是編寫可讀代碼的最有效工具之一可能還有其他。我知道很多函數(shù)式編程編程者會(huì)認(rèn)為形式主義本身有助于學(xué)習(xí)。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson?。 禮ou-Dont-Know-JS》作者 關(guān)于譯者:這是一個(gè)流淌著滬江血液...

    omgdog 評(píng)論0 收藏0
  • 編程書單:十本Python編程語言入門書籍

    摘要:本文與大家分享一些編程語言的入門書籍,其中不乏經(jīng)典。全書貫穿的主體是如何思考設(shè)計(jì)開發(fā)的方法,而具體的編程語言,只是提供一個(gè)具體場(chǎng)景方便介紹的媒介。入門入門容易理解而且讀起來幽默風(fēng)趣,對(duì)于編程初學(xué)者和語言新手而言是理想的書籍。 本文與大家分享一些Python編程語言的入門書籍,其中不乏經(jīng)典。我在這里分享的,大部分是這些書的英文版,如果有中文版的我也加上了。有關(guān)書籍的介紹,大部分截取自是官...

    desdik 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<