国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

Hwg / 784人閱讀

摘要:想閱讀更多優(yōu)質(zhì)文章請猛戳博客一年百來篇優(yōu)質(zhì)文章等著你本系列的第一篇學(xué)會使用函數(shù)式編程的程序員第部分組合函數(shù)作為程序員,我們是懶惰的。柯里化又稱部分求值。一旦使用函數(shù)式語言,任何東西都是不可變的。在函數(shù)式語言中,這個函數(shù)稱為。

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

本系列的第一篇:

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

組合函數(shù) (Function Composition)

作為程序員,我們是懶惰的。我們不想構(gòu)建、測試和部署我們編寫的一遍又一遍的代碼。我們總是試圖找出一次性完成工作的方法,以及如何重用它來做其他事情。

代碼重用聽起來很棒,但是實現(xiàn)起來很難。如果代碼業(yè)務(wù)性過于具體,就很難重用它。如時代碼太過通用簡單,又很少人使用。所以我們需要平衡兩者,一種制作更小的、可重用的部件的方法,我們可以將其作為構(gòu)建塊來構(gòu)建更復(fù)雜的功能。

在函數(shù)式編程中,函數(shù)是我們的構(gòu)建塊。每個函數(shù)都有各自的功能,然后我們把需要的功能(函數(shù))組合起來完成我們的需求,這種方式有點像樂高的積木,在編程中我們稱為 組合函數(shù)

看下以下兩個函數(shù):

var add10 = function(value) {
    return value + 10;
};
var mult5 = function(value) {
    return value * 5;
};

上面寫法有點冗長了,我們用箭頭函數(shù)改寫一下:

var add10 = value => value + 10;
var mult5 = value => value * 5;

現(xiàn)在我們需要有個函數(shù)將傳入的參數(shù)先加上 10 ,然后在乘以 5, 如下:

var mult5AfterAdd10 = value => 5 * (value + 10)

盡管這是一個非常簡單的例子,但仍然不想從頭編寫這個函數(shù)。首先,這里可能會犯一個錯誤,比如忘記括號。第二,我們已經(jīng)有了一個加 10 的函數(shù) add10 和一個乘以 5 的函數(shù) mult5 ,所以這里我們就在寫已經(jīng)重復(fù)的代碼了。

使用函數(shù) add10mult5 來重構(gòu) mult5AfterAdd10

var mult5AfterAdd10 = value => mult5(add10(value));

我們只是使用現(xiàn)有的函數(shù)來創(chuàng)建 mult5AfterAdd10,但是還有更好的方法。

在數(shù)學(xué)中, f ° g 是函數(shù)組合,叫作“f 由 g 組合”,或者更常見的是 “f after g”。 因此 (f ° g)(x) 等效于f(g(x)) 表示調(diào)用 g 之后調(diào)用 f

在我們的例子中,我們有 mult5 ° add10 或 “add10 after mult5”,因此我們的函數(shù)的名稱叫做 mult5AfterAdd10。由于Javascript本身不做函數(shù)組合,看看 Elm 是怎么寫的:

add10 value =
    value + 10
mult5 value =
    value * 5
mult5AfterAdd10 value =
    (mult5 << add10) value

Elm 中 << 表示使用組合函數(shù),在上例中 value 傳給函數(shù) add10 然后將其結(jié)果傳遞給 mult5。還可以這樣組合任意多個函數(shù):

f x =
   (g << h << s << r << t) x

這里 x 傳遞給函數(shù) t,函數(shù) t 的結(jié)果傳遞給 r,函數(shù) t 的結(jié)果傳遞給 s,以此類推。在Javascript中做類似的事情,它看起來會像 g(h(s(r(t(x))))),一個括號噩夢。

Point-Free Notation

Point-Free Notation就是在編寫函數(shù)時不需要指定參數(shù)的編程風(fēng)格。一開始,這風(fēng)格看起來有點奇怪,但是隨著不斷深入,你會逐漸喜歡這種簡潔的方式。

multi5AfterAdd10 中,你會注意到 value 被指定了兩次。一次在參數(shù)列表,另一次是在它被使用時。

// 這個函數(shù)需要一個參數(shù)

mult5AfterAdd10 value =
    (mult5 << add10) value
    

但是這個參數(shù)不是必須的,因為該函數(shù)組合的最右邊一個函數(shù)也就是 add10 期望相同的參數(shù)。下面的 point-free 版本是等效的:

// 這也是一個需要1個參數(shù)的函數(shù)

mult5AfterAdd10 =
    (mult5 << add10)

使用 point-free 版本有很多好處。

首先,我們不需要指定冗余的參數(shù)。由于不必指定參數(shù),所以也就不必考慮為它們命名。

由于更簡短使得更容易閱讀。本例比較簡單,想象一下如果一個函數(shù)有多個參數(shù)的情況。

天堂里的煩惱

到目前為止,我們已經(jīng)了解了組合函數(shù)如何工作以及如何通過 point-free 風(fēng)格使函數(shù)簡潔、清晰、靈活。

現(xiàn)在,我們嘗試將這些知識應(yīng)用到一個稍微不同的場景。想象一下我使用 add 來替換 add10

add x y =
    x + y
mult5 value =
    value * 5

現(xiàn)在如何使用這兩個函數(shù)來組合函數(shù) mult5After10 呢?

我們可能會這樣寫:

-- 這是錯誤的!!!

mult5AfterAdd10 =
    (mult5 << add) 10 

但這行不通。為什么? 因為 add 需要兩個參數(shù)。

這在 Elm 中并不明顯,請嘗試用Javascript編寫:

var mult5AfterAdd10 = mult5(add(10)); // 這個行不通

這段代碼是錯誤的,但是為什么?

因為這里 add 函數(shù)只能獲取到兩個參數(shù)(它的函數(shù)定義中指定了兩個參數(shù))中的一個(實際只傳遞了一個參數(shù)),所以它會將一個錯誤的結(jié)果傳遞給 mult5。這最終會產(chǎn)生一個錯誤的結(jié)果。

事實上,在 Elm 中,編譯器甚至不允許你編寫這種格式錯誤的代碼(這是 Elm 的優(yōu)點之一)。

我們再試一次:

var mult5AfterAdd10 = y => mult5(add(10, y)); // not point-free

這個不是point-free風(fēng)格但是我覺得還行。但是現(xiàn)在我不再僅僅組合函數(shù)。我在寫一個新函數(shù)。同樣如果這個函數(shù)更復(fù)雜,例如,我想使用一些其他的東西來組合mult5AfterAdd10,我真的會遇到麻煩。

由于我們不能將這個兩個函數(shù)對接將會出現(xiàn)函數(shù)組合的作用受限。這太糟糕了,因為函數(shù)組合是如此強大。

如果我們能提前給add函數(shù)一個參數(shù)然后在調(diào)用 mult5AfterAdd10 時得到第二個參數(shù)那就更好了。這種轉(zhuǎn)化我們叫做 柯里化

柯里化 (Currying)

Currying 又稱部分求值。一個 Currying 的函數(shù)首先會接受一些參數(shù),接受了這些參數(shù)之后,該函數(shù)并不會立即求值,而是繼續(xù)返回另外一個函數(shù),剛才傳入的參數(shù)在函數(shù)形成的閉包中被保存起來。待到函數(shù)被真正需要求值的時候,之前傳入的所有參數(shù)都會被一次性用于求值

上例我們在組合函數(shù) mult5add(in) 時遇到問題的是,mult5 使用一個參數(shù),add 使用兩個參數(shù)。我們可以通過限制所有函數(shù)只取一個參數(shù)來輕松地解決這個問題。我只需編寫一個使用兩個參數(shù)但每次只接受一個參數(shù)的add函數(shù),函數(shù)柯里化就是幫我們這種工作的。

柯里化函數(shù)一次只接受一個參數(shù)。

我們先賦值 add 的第1個參數(shù),然后再組合上 mult5,得到 mult5AfterAdd10 函數(shù)。當(dāng) mult5AfterAdd10 函數(shù)被調(diào)用的時候,add 得到了它的第 2 個參數(shù)。

JavaScript 實現(xiàn)方式如下:

var add = x => y => x + y

此時的 add 函數(shù)先后分兩次得到第 1 個和第 2 個參數(shù)。具體地說,add函數(shù)接受單參x,返回一個也接受單參 y的函數(shù),這個函數(shù)最終返回 x+y 的結(jié)果。

現(xiàn)在可以利用這個 add 函數(shù)來實現(xiàn)一個可行的 mult5AfterAdd10* :

var compose = (f, g) => x => f(g(x));
var mult5AfterAdd10 = compose(mult5, add(10));

compose 有兩個參數(shù) fg,然后返回一個函數(shù),該函數(shù)有一個參數(shù) x,并傳給函數(shù) f,當(dāng)函數(shù)被調(diào)用時,先調(diào)用函數(shù) g,返回的結(jié)果作為函數(shù) f的參數(shù)。

總結(jié)一下,我們到底做了什么?我們就是將簡單常見的add函數(shù)轉(zhuǎn)化成了柯里化函數(shù),這樣add函數(shù)就變得更加自由靈活了。我們先將第1個參數(shù)10輸入,而當(dāng)mult5AfterAdd10函數(shù)被調(diào)用的時候,最后1個參數(shù)才有了確定的值。

柯里化與重構(gòu)(Curring and Refactoring)

函數(shù)柯里化允許和鼓勵你分隔復(fù)雜功能變成更小更容易分析的部分。這些小的邏輯單元顯然是更容易理解和測試的,然后你的應(yīng)用就會變成干凈而整潔的組合,由一些小單元組成的組合。

例如,我們有以下兩個函數(shù),它們分別將輸入字符串用單花括號和雙花括號包裹起來:

bracketed = function (str) {
  retrun "{" + str + "}"
}
    
doubleBracketed = function (str) {
  retrun "{{" + str + "}}"
} 

調(diào)用方式如下:

var bracketedJoe =  bracketed("小智")

var doubleBracketedJoe =  doubleBracketed("小智")

可以將 bracketdoubleBracket 轉(zhuǎn)化為更變通的函數(shù):

generalBracket = function( prefix , str ,suffix ) {
  retrun  prefix ++ str ++ suffix
}

但每次我們調(diào)用 generalBracket 函數(shù)的時候,都得這么傳參:

var bracketedJoe = generalBracket("{", "小智", "}")

var doubleBracketedJoe = generalBracket("{{", "小智", "}}")

之前參數(shù)只需要輸入1個,但定義了2個獨立的函數(shù);現(xiàn)在函數(shù)統(tǒng)一了,每次卻需要傳入3個參數(shù),這個不是我們想要的,我們真正想要的是兩全其美。

因為生成小括號雙括號功能但一,重新調(diào)整一下 我們將 generalBracket 三個參數(shù)中的 prefix,str 各柯里化成一個函數(shù),如下:

generalBracket = function( prefix ) {
  return  function( suffix ){
      return function(str){
          return prefix + str + suffix
      }
  }
}

這樣,如果我們要打印單括號或者雙括號,如下:

// 生成單括號
var bracketedJoe = generalBracket("{")("}")
bracketedJoe("小智") // {小智}

// 生成雙括號
var bracketedJoe = generalBracket("{{")("}}")
bracketedJoe("小智") // {{小智}}
 

常見的函數(shù)式函數(shù)(Functional Function)

函數(shù)式語言中3個常見的函數(shù):Map,Filter,Reduce

如下JavaScript代碼:

for (var i = 0; i < something.length; ++i) {
      // do stuff
 }
 

這段代碼存在一個很大的問題,但不是bug。問題在于它有很多重復(fù)代碼(boilerplate code)。如果你用命令式語言來編程,比如Java,C#,JavaScript,PHP,Python等等,你會發(fā)現(xiàn)這樣的代碼你寫地最多。這就是問題所在

現(xiàn)在讓我們一步一步的解決問題,最后封裝成一個看不見 for 語法函數(shù):

先用名為 things 的數(shù)組來修改上述代碼:

var things = [1, 2, 3, 4];
for (var i = 0; i < things.length; ++i) {
    things[i] = things[i] * 10; // 警告:值被改變!
}
console.log(things); // [10, 20, 30, 40]

這樣做法很不對,數(shù)值被改變了!

在重新修改一次:

var things = [1, 2, 3, 4];
var newThings = [];
for (var i = 0; i < things.length; ++i) {
    newThings[i] = things[i] * 10;
}
console.log(newThings); // [10, 20, 30, 40]

這里沒有修改things數(shù)值,但卻卻修改了newThings。暫時先不管這個,畢竟我們現(xiàn)在用的是 JavaScript。一旦使用函數(shù)式語言,任何東西都是不可變的。

現(xiàn)在將代碼封裝成一個函數(shù),我們將其命名為 map,因為這個函數(shù)的功能就是將一個數(shù)組的每個值映射(map)到新數(shù)組的一個新值。

var map = (f, array) => {
    var newArray = [];
    for (var i = 0; i < array.length; ++i) {
        newArray[i] = f(array[i]);
    }
    return newArray;
};

函數(shù) f 作為參數(shù)傳入,那么函數(shù) map 可以對 array 數(shù)組的每項進行任意的操作。

現(xiàn)在使用 map 重寫之前的代碼:

var things = [1, 2, 3, 4];
var newThings = map(v => v * 10, things);

這里沒有 for 循環(huán)!而且代碼更具可讀性,也更易分析。

現(xiàn)在讓我們寫另一個常見的函數(shù)來過濾數(shù)組中的元素:

var filter = (pred, array) => {
    var newArray = [];
for (var i = 0; i < array.length; ++i) {
        if (pred(array[i]))
            newArray[newArray.length] = array[i];
    }
    return newArray;
};

當(dāng)某些項需要被保留的時候,斷言函數(shù) pred 返回TRUE,否則返回FALSE。

使用過濾器過濾奇數(shù):

var isOdd = x => x % 2 !== 0;
var numbers = [1, 2, 3, 4, 5];
var oddNumbers = filter(isOdd, numbers);
console.log(oddNumbers); // [1, 3, 5]

比起用 for 循環(huán)的手動編程,filter 函數(shù)簡單多了。最后一個常見函數(shù)叫reduce。通常這個函數(shù)用來將一個數(shù)列歸約(reduce)成一個數(shù)值,但事實上它能做很多事情。

在函數(shù)式語言中,這個函數(shù)稱為 fold

var reduce = (f, start, array) => {
    var acc = start;
    for (var i = 0; i < array.length; ++i)
        acc = f(array[i], acc); // f() 有2個參數(shù)
    return acc;
});

reduce函數(shù)接受一個歸約函數(shù) f,一個初始值 start,以及一個數(shù)組 array

這三個函數(shù),map,filter,reduce能讓我們繞過for循環(huán)這種重復(fù)的方式,對數(shù)組做一些常見的操作。但在函數(shù)式語言中只有遞歸沒有循環(huán),這三個函數(shù)就更有用了。附帶提一句,在函數(shù)式語言中,遞歸函數(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://m.specialneedsforspecialkids.com/yun/100440.html

相關(guān)文章

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

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

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

    摘要:函數(shù)式編程的目標(biāo)是盡量寫更多的純函數(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
  • 翻譯連載 |《你不知道JS》姊妹篇 |《JavaScript 輕量級函數(shù)編程》- 1 章:

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

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

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

    desdik 評論0 收藏0

發(fā)表評論

0條評論

Hwg

|高級講師

TA的文章

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