中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

函數(shù)式編程中的 “函數(shù)們”

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

函數(shù)式編程中函數(shù)有三種不同的解讀方式,分別為純函數(shù)、高階函數(shù)和一等函數(shù)。本文分別對這三者的概念、應(yīng)用和聯(lián)系進(jìn)行詳解。

純函數(shù)

定義:

1. 相同的輸入必定產(chǎn)生相同的輸出

2. 在計(jì)算的過程中,不會(huì)產(chǎn)生副作用

滿足上述兩個(gè)條件,我們就說該函數(shù)是純函數(shù)。

純函數(shù)也即數(shù)學(xué)意義上的函數(shù),表達(dá)的是數(shù)據(jù)之間的轉(zhuǎn)換(映射)關(guān)系,而非計(jì)算步驟的詳述。數(shù)學(xué)函數(shù)的定義:

函數(shù)通常由定義域 X 、值域 Y, 以及定義域到值域的映射 ff: X -> Y )組成。

純函數(shù)讓我們對寫出的函數(shù)具有完全的控制能力。純函數(shù)的結(jié)果 必須 只依賴于輸入?yún)?shù),不受外部環(huán)境的影響;同時(shí)純函數(shù)在計(jì)算結(jié)果的過程中,也不會(huì)影響(污染)外部環(huán)境,即不會(huì)產(chǎn)生副作用。

函數(shù)組合

純函數(shù)定義中的兩個(gè)條件保證了它(的計(jì)算過程)與外界是完全隔離,這也是函數(shù)組合的基礎(chǔ)。

只有函數(shù)組合中的所有函數(shù)都是純函數(shù),我們組合起來的新函數(shù)才會(huì)是純函數(shù)。我們可以對使用純函數(shù)組合出來的新函數(shù)從數(shù)學(xué)上證明(推導(dǎo))其正確性,而無需借助大量的單元測試。

只要在函數(shù)組合時(shí)引入一個(gè)非純函數(shù),整個(gè)組合出來的函數(shù)將淪為非純函數(shù)。如果將函數(shù)組合比作管道的拼接,只要組成管道的任何一小節(jié)有泄露或者外部注入,我們便失去了對整條管道的完全控制。

要想實(shí)現(xiàn)函數(shù)組合,還需要滿足連續(xù)性,描述如下:

因?yàn)榧兒瘮?shù)可以看作定義域到值域映射,待組合的函數(shù)中,上一個(gè)函數(shù)的值域須等于下一個(gè)函數(shù)的定義域,也即上一個(gè)函數(shù)的輸出(類型)等于下一個(gè)的輸入(類型)。

假設(shè)有兩個(gè)函數(shù): f: X -> Yg: Y -> Z ,只有 codomain(f) = domain(g) 時(shí), fg 才可以組合。

引用透明及緩存

在不改變整個(gè)程序行為的情況下,如果能將其中的一段代碼替換為其執(zhí)行的結(jié)果,我們就說這段代碼是引用透明的。

因此,執(zhí)行一段引用透明的代碼(函數(shù)),對于相同的參數(shù),總是給出相同的結(jié)果。我們也稱這樣的函數(shù)(代碼)為純函數(shù)。

引用透明的一個(gè)典型應(yīng)用即函數(shù)緩存。我們可以將已經(jīng)執(zhí)行過的函數(shù)輸入值緩存起來,下次調(diào)用時(shí),若輸入值相同,直接跳過計(jì)算過程,用緩存結(jié)果代替計(jì)算結(jié)果返回即可。

函數(shù)緩存的實(shí)現(xiàn)依賴于閉包,而閉包的實(shí)現(xiàn)又依賴于高階函數(shù),高階函數(shù)的實(shí)現(xiàn)又依賴于一等函數(shù)。我們按照這條依賴鏈,從里往外依次對它們進(jìn)行講解。

一等函數(shù)(First Class Functions)

程序語言會(huì)對基本元素的使用方式進(jìn)行限制,帶有最少限制的元素被稱為一等公民,其擁有的 “權(quán)利” 如下:

  • 可以使用變量命名;
  • 可以提供給函數(shù)作為參數(shù);
  • 可以由函數(shù)作為結(jié)果返回;
  • 可以包含在數(shù)據(jù)結(jié)構(gòu)中;

乍一看,我們應(yīng)該首先會(huì)想到程序中的基本數(shù)據(jù)結(jié)構(gòu)(如 number、array、object 等)是一等公民。如果函數(shù)也被視為一等公民,我們便可以像使用普通數(shù)據(jù)一樣對其使用變量命名,作為參數(shù)或返回值使用,或者將其包含在數(shù)據(jù)結(jié)構(gòu)中。在這里函數(shù)和數(shù)據(jù)的邊界開始變得不再那么分明了。函數(shù)被視為一等公民后,其能力和適用范圍被大大擴(kuò)展了。

下面使用 JavaScript 對上面第一條和第四條 “權(quán)利” 進(jìn)行講解。第二、三條與高階函數(shù)密切相關(guān),將放到下一節(jié)的高階函數(shù)中講解。

使用變量命名

const square = x => x * x

上面代碼定義了一個(gè)求平方值的函數(shù),并將其賦給了 square 變量。

可以包含在數(shù)據(jù)結(jié)構(gòu)中

Ramda 中有一個(gè)API:evolve ,其接受的首個(gè)參數(shù)便是一個(gè)屬性值為函數(shù)的對象。evolve 函數(shù)會(huì)遞歸地對 “待處理對象” 的屬性進(jìn)行變換,變換的方式由 transformation 內(nèi)置函數(shù)屬性值的對象定義。示例如下(示例中的 R.xxx 都是 Ramda 中的API,相關(guān)API的功能可以參考Ramda 文檔):

var tomato  = {name: 'Tomato', data: {elapsed: 100, remaining: 1400}, id:123};
var transformations = {
  name: R.toUpper,
  data: {elapsed: R.add(1), remaining: R.add(-1)}
};

R.evolve(transformations)(tomato);
//=> {name: 'TOMATO', data: {elapsed: 101, remaining: 1399}, id:123}

高階函數(shù)

定義:

使用函數(shù)作為輸入?yún)?shù),或者返回結(jié)果為函數(shù)的函數(shù),被稱為高階函數(shù)。

作為參數(shù)或返回值的函數(shù),是一等函數(shù)的應(yīng)用之一。高階函數(shù)以一等函數(shù)作為基礎(chǔ),只有支持一等函數(shù)的語言才能進(jìn)行高階函數(shù)編程。

以熟悉的 filter 函數(shù)為例,我們可以用 filter 對列表中的元素進(jìn)行過濾,篩選出符合條件的元素。filter 的類型簽名和示例代碼如下:

filter :: (a → Boolean) → [a] → [a]
const isEven = n => n % 2 === 0;

const filterEven = R.filter(isEven);

filterEven([1, 2, 3, 4]); //=> [2, 4]

filter 接受一個(gè)判斷函數(shù)(判斷輸入值是否為偶數(shù)) isEven ,返回一個(gè)過濾出偶數(shù)的函數(shù) filterEven 。

閉包

定義:

閉包是由函數(shù)及該函數(shù)捕獲的其上下文中的自由變量組成的記錄

舉例講:

function add(x) {
  const xIn = x;
  return function addInner(y) {
    return xIn + y;
  }
}
const inc = add(1);
inc(8); //=> 9;

const plus2 = add(2);
plus2(8); //=> 10;

上述代碼中返回的函數(shù) addInner 及由其捕獲的在其上下文中定義的自由變量 xIn ,便組成了一個(gè)閉包。

上述代碼中最外層的 add 函數(shù)是一個(gè)高階函數(shù),其返回值為一等函數(shù) addInner 。

其實(shí) add 函數(shù)的參數(shù) x 也是 addInner 上下文的一部分,所以 ‘xIn’ 也就沒有存在的必要了, add 代碼優(yōu)化如下:

function add(x) {
  return function addInner(y) {
    return x + y;
  }
}

借助于箭頭函數(shù),我們可以進(jìn)一步優(yōu)化 add 的實(shí)現(xiàn):

const add = x => y => x + y

是不是非常簡潔?由此我們可以一窺函數(shù)式編程強(qiáng)大的表達(dá)能力。

閉包主要用來做數(shù)據(jù)緩存,而數(shù)據(jù)緩存應(yīng)用非常廣泛:包括函數(shù)工廠模式、模擬擁有私有變量的對象、函數(shù)緩存、還有大名鼎鼎的柯里化。

其實(shí)上述代碼中 add 函數(shù)便是柯里化形式的函數(shù)。

上述代碼中的 const inc = add(1); 和 const plus2 = add(2); 是一種函數(shù)工廠模式,通過向 add 函數(shù)傳入不同的參數(shù),便會(huì)產(chǎn)生功能不同的函數(shù)。函數(shù)工廠可以提高函數(shù)的抽象和復(fù)用能力。

例如我們有一個(gè)如下形式的 Ajax 請求函數(shù):

const ajax = method => type => query => { ... };

const get = ajax('GET');
const post = ajax('POST');

const getJson = get('json');
const getHtml = ajax('GET')('text/html') = get('text/html');

我們抽象出了最一般的 ajax 請求函數(shù),在具體應(yīng)用時(shí),我們用能通過函數(shù)工廠生產(chǎn)出作用不同的函數(shù)。

通過上面幾個(gè)小節(jié),我們講解純函數(shù)(數(shù)學(xué)意義上的函數(shù))、一等函數(shù)、高階函數(shù),還有閉包,下面通過集上述所有概念于一身的 函數(shù)緩存 ,來結(jié)束函數(shù)式編程中的 ”函數(shù)們“ 的論述。

函數(shù)緩存 memoize

函數(shù)實(shí)現(xiàn):

const memoize = pureFunc => {
  const cache = {};
  return function() {
    const argStr = JSON.stringify(arguments);
    cache[argStr] = cache[argStr] || pureFunc.apply(pureFunc, arguments);
    return cache[argStr];
  };
};

memoize 的功能是對傳入函數(shù) pureFunc 進(jìn)行緩存,返回緩存版本的 pureFunc 。當(dāng)我們使用參數(shù)調(diào)用緩存的函數(shù)時(shí),緩存的函數(shù)會(huì)到 cache 中查找該參數(shù)是否被緩存過,如果有緩存,則不需要再次計(jì)算,直接返回已緩存值,否則對本次輸入的參數(shù)進(jìn)行計(jì)算,緩存計(jì)算的結(jié)果以備后用,然后將結(jié)果返回。

memoize 只有對純函數(shù)的緩存才有意義。因?yàn)榧兒瘮?shù)是引用透明的,其輸出只依賴于輸入,并且計(jì)算過程不會(huì)影響外部環(huán)境。

舉一個(gè)極端的例子,假如我們有一個(gè)隨機(jī)數(shù)字生成函數(shù) random() , 如果對其進(jìn)行了緩存:

const memoizedRandom = memoize(random);

memoizedRandom 除了第一次生成一個(gè)隨機(jī)值外,隨后的調(diào)用都返回第一次緩存的值,這樣就失去了 random 的意義。再假如,我們對終端字符輸入函數(shù) getchar() 進(jìn)行了緩存,每次調(diào)用都會(huì)是第一次獲取的字母。

memoize 內(nèi)部實(shí)現(xiàn)了一個(gè)閉包的創(chuàng)建。返回的緩存函數(shù)和自由變量 cache 共同構(gòu)成了一個(gè)閉包。自由變量 cached 用于對已經(jīng)計(jì)算過的數(shù)據(jù)(參數(shù))的緩存。而閉包本身是由高階函數(shù)和一等函數(shù)實(shí)現(xiàn)的。

總結(jié)

本文對函數(shù)式編程中的 “函數(shù)們” 做了詳細(xì)解釋:純函數(shù)、一等函數(shù)、高階函數(shù),并展示了它們的應(yīng)用。其中純函數(shù)是函數(shù)組合的基礎(chǔ);一等函數(shù)是高階函數(shù)的實(shí)現(xiàn)基礎(chǔ),一等函數(shù)和高階函數(shù)又是閉包的實(shí)現(xiàn)基礎(chǔ)。

最后通過函數(shù)緩存函數(shù) memoize 將純函數(shù)、一等函數(shù)、高階函數(shù)和閉包聯(lián)系起來,用函數(shù)式編程中的 “函數(shù)們” (函數(shù)式三鏢客)的一次 “聯(lián)合行動(dòng)” 結(jié)束本文。

參考文檔

What is a Function? .

Functional Programming .

Referential Transparency .

 

來自:http://www.techug.com/post/functions-in-functional-programming.html

 

標(biāo)簽: 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:OAuth 2.0 理解

下一篇:分布式事務(wù)的總結(jié)與思考