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

資訊專欄INFORMATION COLUMN

成為一名函數(shù)式碼農(nóng)系列之五

econi / 582人閱讀

摘要:本文為本人參與的前端早讀課公眾號(hào)成為函數(shù)式碼農(nóng)系列翻譯的第五篇,第六篇仍在翻譯中,以下為其它五篇的地址。然而,關(guān)于多線程,存在兩個(gè)主要的問題。首先,它們必須是純函數(shù)。類型意味著一個(gè)值都為類型的列表,意味著一個(gè)值都為類型的列表。

本文為本人參與的前端早讀課公眾號(hào)《成為函數(shù)式碼農(nóng)》系列翻譯的第五篇,第六篇仍在翻譯中,以下為其它五篇的地址。

成為一名函數(shù)式碼農(nóng)系列之一

成為一名函數(shù)式碼農(nóng)系列之二

成為一名函數(shù)式碼農(nóng)系列之三

成為一名函數(shù)式碼農(nóng)系列之四

成為一名函數(shù)式碼農(nóng)系列之六

原文地址 譯者:墨白 校對:野草

剛開始學(xué)習(xí)函數(shù)式編程時(shí),理解函數(shù)式編程的核心概念是最重要的,有些時(shí)候也是最難的一步。但其實(shí)沒必要一定如此。這個(gè)沒有正確的答案。

引用透明(referential transparency)

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

在代數(shù)中,你有一個(gè)下面的公式:
y = x + 10

然后被告知:
x = 3

你可以把x的值帶入到之前的方程中,得到:
y = 3 + 10

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

下面是一個(gè)Elm的方法,在傳入的字符串兩邊加上單引號(hào):

quote str =
  """ ++ str ++ """

下面是使用它的代碼:

findError key =
  "Unable to find " ++ (quote key)

代碼中,當(dāng)查詢key值失敗時(shí),findError構(gòu)建了一個(gè)報(bào)錯(cuò)信息。

因?yàn)?b>quote方法是純函數(shù),我們可以簡單地將quote函數(shù)體(僅僅只是個(gè)表達(dá)式)替換掉在findError中的方法調(diào)用:

findError key =
  "Unable to find " ++ (""" ++ str ++ """)

這就是我所說的“反向重構(gòu)”(它對我而言有更多的意義),一個(gè)可以被程序員或者程序(例如編譯器和測試程序)用來推理代碼的過程。

這在推導(dǎo)遞歸函數(shù)時(shí)尤其有用。

執(zhí)行順序

大部分程序是單線程的,即有且只有一段代碼在當(dāng)前執(zhí)行。即使你有多線程的程序,大部分程序仍然阻塞等待I/O去完成,例如,file,network等等。

這也是當(dāng)我們編寫代碼的時(shí)候,我們很自然考慮按次序來編寫代碼:

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

在這個(gè)例子中,有兩個(gè)獨(dú)立的操作:拿黃油以及加熱面包。它們在step9時(shí)開始變得相互依賴。

我們可以在step1到6的時(shí)候做step7和8因?yàn)樗鼈冎g相互獨(dú)立。

但當(dāng)我們開始做的時(shí)候,事情開始復(fù)雜了:

線程1:
--------
1. 拿到面包
2. 把2片面包放入烤面包機(jī)
3. 選擇加熱時(shí)間
4. 向下推桿
5. 等待面包片彈出
6. 取出烤面包

線程2:
1. 拿黃油
2. 拿黃油刀
3. 等待線程1完成
4. 制作黃油面包

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

我們完全可以不去思考這些復(fù)雜的,只讓我們的程序單線程運(yùn)行,這更簡單。

但是,只要能夠提升我們程序的效率,那就是值得的,我們要付出努力來寫好多線程程序。

然而,關(guān)于多線程,存在兩個(gè)主要的問題。首先,多線程程序非常難寫、讀、理解、測試以及debug。

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

但是,如果運(yùn)行順序并不重要并且一切都是并行執(zhí)行的呢?

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

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

這里的buildMessage接受messagevalue,然后,生成大寫的message,冒號(hào)和在單引號(hào)中value。

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

對于獨(dú)立,有兩點(diǎn)必須必須滿足。首先,它們必須是純函數(shù)。這很重要,因?yàn)樗鼈儽仨毑粫?huì)被其它方法的運(yùn)行影響到。

如果它們不是純函數(shù),那么我們永遠(yuǎn)不可能知道它們是否獨(dú)立。那種情況下,我們不得不依賴于它們在程序中調(diào)用的順序來確定它們的執(zhí)行順序。這是所有命令式語言的工作原理。

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

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

因此,這兩個(gè)方法可以在任何順序下執(zhí)行。

編譯器可以自行決定執(zhí)行的順序,而不需要程序員的人為參與。這只有在純函數(shù)式編程語言中才適用,因?yàn)樵谝话憔幊陶Z言中是很難去(不是不可能)預(yù)估不同順序帶來的副作用。

純函數(shù)式語言里面,執(zhí)行的順序是可以由編譯器決定的

鑒于無法一再加快CPU的運(yùn)行速度,這一做法是非常有利的。生產(chǎn)商也不斷增加CPU內(nèi)核芯片的數(shù)量,這就意味著可以在硬件這一層面實(shí)現(xiàn)代碼的并行處理。

但遺憾的是,我們無法通過命令式的語言充分利用這些芯片,而只是發(fā)揮了它們很小一部分的功能。如果要充分利用就要徹底改變程序的體系結(jié)構(gòu)。

使用純函數(shù)語言,我們就有希望在不改變?nèi)魏未a的情況下充分地發(fā)揮CPU芯片的功能并取得良好成效。

類型注釋

在靜態(tài)類型語言中,類型是內(nèi)聯(lián)定義的。 這里通過一些Java代碼來說明:


public static String quote(String str) {

return """ + str + """;

}


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

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

我已經(jīng)給定義類型的字段加粗了以使其更加顯眼,但它們看來去仍然和函數(shù)定義糾纏在一起。你不得不很小心地去找到這些變量的名字。

如果是用神奇的動(dòng)態(tài)類型語言,這不是一個(gè)問題。在JavaScript中,我們這樣寫代碼:

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

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

除非程序運(yùn)行,否則我們發(fā)現(xiàn)不了這樣的問題,而這樣的問題也可能在代碼上線之后幾個(gè)月才能出現(xiàn)。而這樣的問題在Java中不會(huì)出現(xiàn),因?yàn)樗鼰o法通過編譯。

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

其實(shí)我們是可以的。下面是一個(gè)帶類型注釋的用Elm寫的方法:

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

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

現(xiàn)在你可能認(rèn)為類型注釋有錯(cuò)字。 我知道我第一次見到它的時(shí)候。 我認(rèn)為第一個(gè) - >應(yīng)該是一個(gè)逗號(hào)。 但并沒有錯(cuò)別字。

當(dāng)你看到它加上隱含的括號(hào),代碼就清晰多了:

add : Int -> (Int -> Int)

這表示,add是一個(gè)方法,它接受單個(gè)Int類型的參數(shù),返回一個(gè)方法,這個(gè)方法接受一個(gè)Int類型的參數(shù),并且返回一個(gè)Int類型的值。

這里還有一個(gè)帶括號(hào)類型注釋的代碼:

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

上面的代碼表示doSomething是一個(gè)方法,它接受單個(gè)類型為String的參數(shù)并且返回一個(gè)函數(shù),返回的函數(shù)接受單個(gè)類型為Int的參數(shù),并且再次返回一個(gè)函數(shù),這次返回的函數(shù)接受一個(gè)類型為String的參數(shù),并且返回一個(gè)String。

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

由于括號(hào)總是隱含在右邊,它們并不是必須。所以我們可以簡寫成:

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

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

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

與下面的并不同:

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

takes2Param是一個(gè)接受兩個(gè)參數(shù)的函數(shù),兩個(gè)參數(shù)都是Int類型。然而,takes1Param需要接受一個(gè)參數(shù),這個(gè)參數(shù)為函數(shù),而函數(shù)需要接受兩個(gè)Int類型的參數(shù)。

下面是map的類型注釋:

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

上面需要括號(hào)是因?yàn)?b>f是(a -> b)類型,即接受類型為a的單個(gè)參數(shù)并返回類型為b的某個(gè)函數(shù).

這里類型a是代指任何類型。 當(dāng)類型是大寫時(shí),它是一個(gè)顯式類型,例如,String。 當(dāng)類型為小寫時(shí),它可以是任何類型。 這里的a可以是String,但也可以是Int。

如果你看到(a -> a),那么,就是指input類型以及output類型是相同的。它們到底是什么類型并不重要,重要的是它們必須匹配。

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

但是一旦a的類型確定了,(TODO the whole signature)a在整段代碼中就必須為這個(gè)類型。例如,如果a是一個(gè)Int,b是一個(gè)String,那么這段代碼就相當(dāng)于:

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

上面就是所有的a都被替換成Int,所有的b都被替換成String。

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

在這個(gè)系列文章的最后,我將會(huì)探討如何使用你在日常生活中學(xué)到的東西,例如,函數(shù)式編程以及Elem。

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

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

相關(guān)文章

  • 文章大雜燴 - 收藏集 - 掘金

    摘要:基礎(chǔ)深度學(xué)習(xí)概念備忘錄后端掘金基礎(chǔ)深度學(xué)習(xí)概念備忘錄翻譯自。否則,試想在你捧著某出版社剛剛翻譯出來的高效編程苦規(guī)范及相關(guān)文檔前端掘金官方規(guī)范歲程序員的獨(dú)家面試經(jīng)歷閱讀掘金創(chuàng)業(yè)失敗后,在找工作。 基礎(chǔ)深度學(xué)習(xí)概念備忘錄 - 后端 - 掘金基礎(chǔ)深度學(xué)習(xí)概念備忘錄翻譯自DeepLearning Cheat Sheet。筆者還是菜鳥一枚,若有謬誤請多多賜教,另外如果希望了解更多機(jī)器學(xué)習(xí)&深度學(xué)...

    wuyumin 評論0 收藏0
  • Python貓薦書系列之五:Python高性能編程

    摘要:鋪墊已了,進(jìn)入今天的正題,貓薦書系列之五高性能編程本書適合已入門還想要進(jìn)階和提高的讀者閱讀。書中列舉了兩個(gè)慘痛的教訓(xùn)華爾街公司騎士資本由于軟件升級引入的錯(cuò)誤,損失億美元公司小時(shí)全球中斷的嚴(yán)重事故。 showImg(https://segmentfault.com/img/bVbm92w?w=6720&h=4480); 稍微關(guān)心編程語言的使用趨勢的人都知道,最近幾年,國內(nèi)最火的兩種語言非...

    channg 評論0 收藏0
  • Python貓薦書系列之五:Python高性能編程

    摘要:鋪墊已了,進(jìn)入今天的正題,貓薦書系列之五高性能編程本書適合已入門還想要進(jìn)階和提高的讀者閱讀。書中列舉了兩個(gè)慘痛的教訓(xùn)華爾街公司騎士資本由于軟件升級引入的錯(cuò)誤,損失億美元公司小時(shí)全球中斷的嚴(yán)重事故。 showImg(https://segmentfault.com/img/bVbm92w?w=6720&h=4480); 稍微關(guān)心編程語言的使用趨勢的人都知道,最近幾年,國內(nèi)最火的兩種語言非...

    馬永翠 評論0 收藏0

發(fā)表評論

0條評論

econi

|高級講師

TA的文章

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