純函數(shù)
一個(gè)函數(shù)在程序執(zhí)行的過(guò)程中除了根據(jù)輸入?yún)?shù)給出運(yùn)算結(jié)果之外沒(méi)有其他的副作用影響,我們可以把這類函數(shù)稱為“純函數(shù)”。純函數(shù)由于不依賴外部變量,使得給定函數(shù)輸入其返回結(jié)果永遠(yuǎn)不變,比如整數(shù)的加法函數(shù),它接收兩個(gè)整數(shù)值并返回一個(gè)整數(shù)值,對(duì)于給定的兩個(gè)整數(shù)值,它的返回值永遠(yuǎn)是相同的整數(shù)值。
副作用
相對(duì)于非純函數(shù),它們帶有副作用,這使得函數(shù)不僅簡(jiǎn)單返回一個(gè)值,還做了其他事情:
- 修改了一個(gè)變量
- 直接修改了數(shù)據(jù)結(jié)構(gòu)
- 設(shè)置了一個(gè)對(duì)象的成員
- 拋出一個(gè)異常或以一個(gè)錯(cuò)誤終止
- 打印到終端或讀取用戶的輸入
- 讀取或?qū)懭胍粋€(gè)文件
- 在屏幕上繪畫(huà)
像是執(zhí)行IO、處理錯(cuò)誤、修改數(shù)據(jù)都屬于副作用,這些副作用可能導(dǎo)致我們編寫的程序難以測(cè)試,進(jìn)而容易產(chǎn)生bug。遵循函數(shù)式編程規(guī)范可以使編程更加模塊化,由于純函數(shù)模塊化的特性,使得程序容易被測(cè)試、復(fù)用、并行化、泛化以及推導(dǎo),這也減少了產(chǎn)生bug的可能。
純函數(shù)的使用將數(shù)據(jù)的創(chuàng)建過(guò)程和處理過(guò)程分離,通過(guò)把副作用推導(dǎo)程序的外層,來(lái)轉(zhuǎn)換任何帶有副作用的函數(shù)。對(duì)于函數(shù)式程序員而言,程序的實(shí)現(xiàn)應(yīng)該有一個(gè)純的內(nèi)核和一層很薄的外圍來(lái)處理副作用。
引用透明
引用透明(referential transparency)的概念對(duì)純函數(shù)進(jìn)行形式化,符合引用透明的表達(dá)式都可以由它的結(jié)果所取代,而不改變?cè)摮绦虻暮x。當(dāng)調(diào)用一個(gè)函數(shù)時(shí)所傳入的參數(shù)是引用透明的,冰鞋函數(shù)調(diào)用也是引用透明的,那么這個(gè)函數(shù)式一個(gè)純函數(shù)。當(dāng)傳入函數(shù)的參數(shù)也是一個(gè)純函數(shù)時(shí),使得高階函數(shù)的組合也是引用透明的,這有利于構(gòu)建更加復(fù)雜的邏輯,而本身程序的計(jì)算結(jié)果是可以進(jìn)行推斷的,不用擔(dān)心環(huán)境對(duì)程序的影響。
對(duì)于程序p,如果它包含的表達(dá)式e滿足引用透明,所有的e都可以替換為它的運(yùn)算結(jié)果而不會(huì)改變程序p的含義。假設(shè)存在一個(gè)函數(shù)f,若變大時(shí)f(x)對(duì)所有引用透明的表達(dá)式x也是引用透明的,那么這個(gè)f是一個(gè)純函數(shù)。
替代模型
引用透明要求函數(shù)不論進(jìn)行了任何操作都可以用它的返回值來(lái)代替。這種限制使得推導(dǎo)一個(gè)程序的求值變得簡(jiǎn)單而自然,稱之為替代模型(substitution model)。如果表達(dá)式是引用透明的,可以想象計(jì)算過(guò)程就像在解代數(shù)方程。展開(kāi)表達(dá)式的每一部分,使用指示對(duì)象替代變量,然后歸約到最簡(jiǎn)單的形式。在這一過(guò)程中,每項(xiàng)都被等價(jià)值所替代,計(jì)算的過(guò)程就是一個(gè)又一個(gè)等價(jià)值所替代的過(guò)程。換句話說(shuō),引用透明使得程序具備了等式推理的能力。
替代模型更容易推理,因?yàn)閷?duì)運(yùn)算的影響純粹是局部的(只對(duì)那些賦值表達(dá)式產(chǎn)生影響),不需要先在內(nèi)心模擬一系列狀態(tài)的更新才理解這一段代碼。只需要理解局部的推理,不必費(fèi)心地去跟蹤函數(shù)執(zhí)行前后的狀態(tài)變化,只用簡(jiǎn)單看一下函數(shù)的定義,把它替換成一個(gè)參數(shù)。
小結(jié)
這一小節(jié),我們了解了純函數(shù)的一些基本概念和其帶來(lái)的好處。我們知道,純函數(shù)時(shí)模塊化的、可組合的,因?yàn)樗鼜摹皩?duì)結(jié)果做什么”和“如果獲取輸入”中分離了計(jì)算本身的邏輯,就像一個(gè)黑盒子。對(duì)輸入的獲取只有一種方式:通過(guò)參數(shù)傳給函數(shù)。輸出也只是簡(jiǎn)單地將計(jì)算結(jié)果返回。把這些關(guān)注點(diǎn)分離開(kāi),計(jì)算也更容易被復(fù)用。我們可以復(fù)用這些邏輯,而不必?fù)?dān)心輸入或輸出對(duì)整個(gè)上下文引起的副作用。
轉(zhuǎn)載請(qǐng)注明作者Jason Ding及其出處
jasonding.top
Github博客主頁(yè)(http://blog.jasonding.top/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡(jiǎn)書(shū)主頁(yè)(http://www.lxweimin.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進(jìn)入我的博客主頁(yè)