成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

scwang90 / 1530人閱讀

摘要:即使你有一個多線程程序,大多數(shù)線程都被阻塞等待完成,例如文件,網(wǎng)絡(luò)等等。但是只要能夠提升我們程序的效率,要付出努力來寫好多線程程序這是值得的。然而,多線程有兩個主要問題多線程程序難于編寫讀取解釋測試和調(diào)試。

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

本系列的其它篇:

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

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

引用透明 (Referential Transparency)

引用透明是一個富有想象力的優(yōu)秀術(shù)語,它是用來描述純函數(shù)可以被它的表達式安全的替換,通過下例來幫助我們理解。

在代數(shù)中,有一個如下的公式:

y = x + 10

接著:

 x = 3

然后帶入表達式:

y = 3 + 10

注意這個方程仍然是有效的,我們可以利用純函數(shù)做一些相同類型的替換。

下面是一個 JavaScript 的方法,在傳入的字符串兩邊加上單引號:

function quote (str) {
  retrun """ + str + """
}

下面是調(diào)用它:

   function findError (key) {
     return "不能找到 " + quote(key)
   }

當查詢 key 值失敗時,findError 返回一個報錯信息。

因為 quote 是純函數(shù),我們可以簡單地將 quote 函數(shù)體(這里僅僅只是個表達式)替換掉在findError中的方法調(diào)用:

   function findError (key) {
     return "不能找到 " + """ + str + """
   }

這個就是通常所說的“反向重構(gòu)”(它對我而言有更多的意義),可以用來幫程序員或者程序(例如編譯器和測試程序)推理代碼的過程一個很好的方法。如,這在推導(dǎo)遞歸函數(shù)時尤其有用的。

執(zhí)行順序 (Execution Order)

大多數(shù)程序都是單線程的,即一次只執(zhí)行一段代碼。即使你有一個多線程程序,大多數(shù)線程都被阻塞等待I/O完成,例如文件,網(wǎng)絡(luò)等等。

這也是當我們編寫代碼的時候,我們很自然考慮按次序來編寫代碼:

1. 拿到面包 
2. 把2片面包放入烤面包機 
3. 選擇加熱時間 
4. 按下開始按鈕 
5. 等待面包片彈出 
6. 取出烤面包 
7. 拿黃油 
8. 拿黃油刀 
9. 制作黃油面包 

在這個例子中,有兩個獨立的操作:拿黃油以及 加熱面包。它們在 步驟9 時開始變得相互依賴。

我們可以將 步驟7步驟8步驟1步驟6 同時執(zhí)行,因為它們彼此獨立。當我們開始做的時候,事情開始復(fù)雜了:

線程一
--------------------------
1. 拿到面包 
2. 把2片面包放入烤面包機 
3. 選擇加熱時間 
4. 按下開始按鈕 
5. 等待面包片彈出 
6. 取出烤面包 

線程二
-------------------------
1. 拿黃油 
2. 拿黃油刀 
3. 等待線程1完成 
4. 取出烤面包 

果線程1失敗,線程2怎么辦? 怎么協(xié)調(diào)這兩個線程? 烤面包這一步驟在哪個線程運行:線程1,線程2或者兩者?

不考慮這些復(fù)雜性,讓我們的程序保持單線程會更容易。但是,只要能夠提升我們程序的效率,要付出努力來寫好多線程程序,這是值得的。

然而,多線程有兩個主要問題:

多線程程序難于編寫、讀取、解釋、測試和調(diào)試。

一些語言,例如JavaScript,并不支持多線程,就算有些語言支持多線程,對它的支持也很弱。

但是,如果順序無關(guān)緊要,所有事情都是并行執(zhí)行的呢?

盡管這聽起來有些瘋狂,但其實并不像聽起來那么混亂。讓我們來看一下 Elm 的代碼來形象的理解它:

buildMessage message value =
    let
        upperMessage =
            String.toUpper message
        quotedValue =
            """ ++ value ++ """
    in
        upperMessage ++ ": " ++ quotedValue

這里的 buildMessage 接受參數(shù) messagevalue,然后,生成大寫的 message和 帶有引號的 value 。

注意到 upperMessagequotedValue 是獨立的。我們怎么知道的呢?

在上面的代碼示例中,upperMessagequotedValue 兩者都是純的并且沒有一個需要依賴其它的輸出。

如果它們不純,我們就永遠不知道它們是獨立的。在這種情況下,我們必須依賴程序中調(diào)用它們的順序來確定它們的執(zhí)行順序。這就是所有命令式語言的工作方式。

第二點必須滿足的就是一個函數(shù)的輸出值不能作為其它函數(shù)的輸入值。如果存在這種情況,那么我們不得不等待其中一個完成才能執(zhí)行下一個。

在本例中,upperMessagequotedValue 都是純的并且沒有一個需要依賴其它的輸出,因此,這兩個函數(shù)可以以任何順序執(zhí)行。

編譯器可以在不需要程序員幫助的情況下做出這個決定。這只有在純函數(shù)式語言中才有可能,因為很難(如果不是不可能的話)確定副作用的后果。

在純函數(shù)語言中,執(zhí)行的順序可以由編譯器決定。

考慮到 CPU 無法一再的加快速度,這種做法非常有利的。別一方面,生產(chǎn)商也不斷增加CPU內(nèi)核芯片的數(shù)量,這意味著代碼可以在硬件層面上并行執(zhí)行。使用純函數(shù)語言,就有希望在不改變?nèi)魏未a的情況下充分地發(fā)揮 CPU 芯片的功能并取得良好成效。

類型注釋 (Type Annotations)

在靜態(tài)類型語言中,類型是內(nèi)聯(lián)定義的。以下是 Java 代碼:

public static String quote(String str) {
    return """ + str + """;
}

注意類型是如何同函數(shù)定義內(nèi)聯(lián)在一起的。當有泛型時,它變的更糟:

private final Map getPerson(Map people, Integer personId) {
   // ...
}

這里使用粗體標出了使它們使用的類型,但它們?nèi)匀粫尯瘮?shù)可讀性降低,你必須仔細閱讀才能找到變量的名稱。

對于動態(tài)類型語言,這不是問題。在 Javascript 中,可以編寫如下代碼:

var getPerson = function(people, personId) {
    // ...
};

這樣沒有任何的的類型信息更易于閱讀,唯一的問題就是放棄了類型檢測的安全特性。這樣能夠很簡單的傳入這些參數(shù),例如,一個 Number 類型的 people 以及一個 Objec t類型的 personId。

動態(tài)類型要等到程序執(zhí)行后才能知道哪里問題,這可能是在發(fā)布的幾個月后。在 Java 中不會出現(xiàn)這種情況,因為它不能被編譯。

但是,假如我們能同時擁有這兩者的優(yōu)異點呢? JavaScript 的語法簡單性以及 Java 的安全性。

事實證明我們可以。下面是 Elm 中的一個帶有類型注釋的函數(shù):

add : Int -> Int -> Int
add x y =
    x + y

請注意類型信息是在多帶帶的代碼行上面的,而正是這樣的分割使得其有所不同。

現(xiàn)在你可能認為類型注釋有錯訓(xùn)。 第一次見到它的時候。 大都認為第一個 -> 應(yīng)該是一個逗號。可以加上隱含的括號,代碼就清晰多了:

add : Int -> (Int -> Int)

上例 add 是一個函數(shù),它接受類型為 Int 的單個參數(shù),并返回一個函數(shù),該函數(shù)接受單個參數(shù) Int類型 并返回一個 Int 類型的結(jié)果。

以下是一個帶括號類型注釋的代碼:

doSomething : String -> (Int -> (String -> String)) 
doSomething prefix value suffix = 
prefix ++ (toString value) ++ suffix

這里 doSomething 是一個函數(shù),它接受 String 類型的單個參數(shù),然后返回一個函數(shù),該函數(shù)接受 Int 類型的單個參數(shù),然后返回一個函數(shù),該函數(shù)接受 String 類型的單個參數(shù),并返回一個字符串。

注意為什么每個方法都只接受一個參數(shù)呢? 這是因為每個方法在 Elm 里面都是柯里化。

因為括號總是指向右邊,它們是不必要的,簡寫如下:

doSomething : String -> Int -> String -> String

當我們將函數(shù)作為參數(shù)傳遞時,括號是必要的。如果沒有它們,類型注釋將是不明確的。例如:

takes2Params : Int -> Int -> String
takes2Params num1 num2 =
    -- do something

非常不同于:

takes1Param : (Int -> Int) -> String
takes1Param f =
    -- do something

takes2Param 函數(shù)需要兩個參數(shù),一個 Int 和另一個 Int,而takes1Param 函數(shù)需要一個參數(shù),這個參數(shù)為函數(shù), 這個函數(shù)需要接受兩個 Int 類型參數(shù)。

下面是 map 的類型注釋:

map : (a -> b) -> List a -> List b
map f list =
    // ...

這里需要括號,因為 f 的類型是(a -> b),也就是說,函數(shù)接受類型 a 的單個參數(shù)并返回類型 b 的某個函數(shù)。

這里類型 a 是任何類型。當類型為大寫形式時,它是顯式類型,例如 String。當一個類型是小寫時,它可以是任何類型。這里 a 可以是字符串,也可以是 Int。

如果你看到 (a -> a) 那就是說輸入類型和輸出類型必須是相同的。它們是什么并不重要,但必須匹配。

但在 map 這一示例中,有這樣一段 (a -> b)。這意味著它既能返回一個不同的類型,也能返回一個相同的類型。

但是一旦 a 的類型確定了,a 在整段代碼中就必須為這個類型。例如,如果 a 是一個 Int,b 是一個 String,那么這段代碼就相當于:

(Int -> String) -> List Int -> List String

這里所有的 a 都換成了 Int,所有的 b 都換成了 String。

List Int 類型意味著一個值都為 Int 類型的列表, List String 意味著一個值都為 String 類型的列表。如果你已經(jīng)在 Java 或者其他的語言中使用過泛型,那么這個概念你應(yīng)該是熟悉的

函數(shù)式 JavaScript

JavaScript 擁有很多類函數(shù)式的特性但它沒有純性,但是我們可以設(shè)法得到一些不變量和純函數(shù),甚至可以借助一些庫。

但這并不是理想的解決方法。如果你不得不使用純特性,為何不直接考慮函數(shù)式語言?

這并不理想,但如果你必須使用它,為什么不從函數(shù)式語言中獲得一些好處呢?

不可變性(Immutability)

首先要考慮的是不變性。在ES2015或ES6中,有一個新的關(guān)鍵詞叫const,這意味著一旦一個變量被設(shè)置,它就不能被重置:

const a = 1;
a = 2; // 這將在Chrome、Firefox或 Node中拋出一個類型錯誤,但在Safari中則不會

在這里,a 被定義為一個常量,因此一旦設(shè)置就不能更改。這就是為什么 a = 2 拋出異常。

const 的缺陷在于它不夠嚴格,我們來看個例子:

const a = {
    x: 1,
    y: 2
};
a.x = 2; // 沒有異常
a = {}; // 報錯

注意到 a.x = 2 沒有拋出異常。const 關(guān)鍵字唯一不變的是變量 a, a 所指向的對象是可變的。

那么Javascript中如何獲得不變性呢?

不幸的是,我們只能通過一個名為 Immutable.js 的庫來實現(xiàn)。這可能會給我們帶來更好的不變性,但遺憾的是,這種不變性使我們的代碼看起來更像 Java 而不是 Javascript。

柯里化與組合 (curring and composition)

在本系列的前面,我們學(xué)習(xí)了如何編寫柯里化函數(shù),這里有一個更復(fù)雜的例子:

const f = a => b => c => d => a + b + c + d

我們得手寫上述柯里化的過程,如下:

console.log(f(1)(2)(3)(4)); // prints 10

括號如此之多,但這已經(jīng)足夠讓Lisp程序員哭了。有許多庫可以簡化這個過程,我最喜歡的是 Ramda。

使用 Ramda 簡化如下:

const f = R.curry((a, b, c, d) => a + b + c + d);
console.log(f(1, 2, 3, 4)); // prints 10
console.log(f(1, 2)(3, 4)); // also prints 10
console.log(f(1)(2)(3, 4)); // also prints 10

函數(shù)的定義并沒有好多少,但是我們已經(jīng)消除了對那些括號的需要。注意,調(diào)用 f 時,可以指定任意參數(shù)。

重寫一下之前的 mult5AfterAdd10 函數(shù):

const add = R.curry((x, y) => x + y);
const mult5 = value => value * 5;
const mult5AfterAdd10 = R.compose(mult5, add(10));

事實上 Ramda 提供了很多輔助函數(shù)來做些簡單常見的運算,比如R.add以及R.multiply。以上代碼我們還可以簡化:

const mult5AfterAdd10 = R.compose(R.multiply(5), R.add(10));

Map, Filter and Reduce

Ramda 也有自己的 map、filterreduce 版本。雖然這些函數(shù)存在于數(shù)組中。這幾個函數(shù)是在 Array.prototype 對象中的,而在 Ramda 中它們是柯里化的

const isOdd = R.flip(R.modulo)(2);
const onlyOdd = R.filter(isOdd);
const isEven = R.complement(isOdd);
const onlyEven = R.filter(isEven);
const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(onlyEven(numbers)); // prints [2, 4, 6, 8]
console.log(onlyOdd(numbers)); // prints [1, 3, 5, 7]

R.modulo 接受2個參數(shù),被除數(shù)和除數(shù)。

isOdd 函數(shù)表示一個數(shù)除 2 的余數(shù)。若余數(shù)為 0,則返回 false,即不是奇數(shù);若余數(shù)為 1,則返回 true,是奇數(shù)。用 R.filp 置換一下 R.modulo 函數(shù)兩個參數(shù)順序,使得 2 作為除數(shù)。

isEven 函數(shù)是 isOdd 函數(shù)的補集。

onlyOdd 函數(shù)是由 isOdd 函數(shù)進行斷言的過濾函數(shù)。當它傳入最后一個參數(shù),一個數(shù)組,它就會被執(zhí)行。

同理,onlyEven 函數(shù)是由 isEven 函數(shù)進行斷言的過濾函數(shù)。

當我們給函數(shù) onlyEvenonlyOd 傳入 numbers,isEvenisOdd 獲得了最后的參數(shù),然后執(zhí)行最終返回我們期望的數(shù)字。

Javascript的缺點

所有的庫和語言增強都已經(jīng)得到了Javascript 的發(fā)展,但它仍然面臨著這樣一個事實:它是一種強制性的語言,它試圖為所有人提供所有的東西。

大多數(shù)前端開發(fā)人員都不得不使用 Javascript,因為這旨瀏覽器也識別的語言。相反,它們使用不同的語言編寫,然后編譯,或者更準確地說,是把其它語言轉(zhuǎn)換成 Javascript。

CoffeeScript 是這類語言中最早的一批。目前,TypeScript 已經(jīng)被 Angular2 采用,Babel可以將這類語言編譯成 JavaScript,越來越多的開發(fā)者在項目中采用這種方式。

但是這些語言都是從 Javascript 開始的,并且只稍微改進了一點。為什么不直接從純函數(shù)語言轉(zhuǎn)換到Javascript呢?

未來期盼

我們不可能知道未來會怎樣,但我們可以做一些有根據(jù)的猜測。以下是作者的一些看法:

能轉(zhuǎn)換成 JavaScript 這類語言會有更加豐富及健壯。

已有40多年歷史的函數(shù)式編程思想將被重新發(fā)現(xiàn),以解決我們當前的軟件復(fù)雜性問題。

目前的硬件,比如廉價的內(nèi)存,快速的處理器,使得函數(shù)式技術(shù)普及成為可能。

PU不會變快,但是內(nèi)核的數(shù)量會持續(xù)增加。

可變狀態(tài)將被認為是復(fù)雜系統(tǒng)中最大的問題之一。

希望這系列文章能幫助你更好容易更好幫助你理解函數(shù)式編程及優(yōu)勢,作者相信函數(shù)式編程是未來趨勢,大家有時間可以多多了解,接著提升你們的技能,然后未來有更好的出路。

原文:

https://medium.com/@cscalfani...

https://medium.com/@cscalfani...

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

你的點贊是我持續(xù)分享好東西的動力,歡迎點贊!

歡迎加入前端大家庭,里面會經(jīng)常分享一些技術(shù)資源。

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/100504.html

相關(guān)文章

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

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

    Hwg 評論0 收藏0
  • 學(xué)會使用函數(shù)編程序員(1部分)

    摘要:函數(shù)式編程的目標是盡量寫更多的純函數(shù),并將其與程序的其他部分隔離開來。在函數(shù)式編程中,是非法的。函數(shù)式編程使用參數(shù)保存狀態(tài),最好的例子就是遞歸。函數(shù)式編程使用遞歸進行循環(huán)。在函數(shù)式編程中,函數(shù)是一級公民。 showImg(https://segmentfault.com/img/bVblxCO?w=1600&h=710); 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等...

    Steven 評論0 收藏0
  • javascript 函數(shù)編程思想

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

    YPHP 評論0 收藏0
  • 編程書單:十本Python編程語言入門書籍

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

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

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

    omgdog 評論0 收藏0

發(fā)表評論

0條評論

scwang90

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<