转自 http://zh.wikipedia.org/zh-tw/%E5%87%BD%E6%95%B0%E5%89%AF%E4%BD%9C%E7%94%A8
在計算機科學中,函數副作用指當調用函數時,除了返回函數值之外,還對主調用函數產生附加的影響。例如修改全局變數(函數外的變數)或修改參數。
函數副作用會給程序設計帶來不必要的麻煩,給程序帶來十分難以查找的錯誤,並且降低程序的可讀性。嚴格的函數式語言要求函數必須無副作用。
下面是函數的副作用相關的幾個概念, 純函數(Pure Function)、非純函數(Impure Function)、引用透明(Referential Transparent)。
純函數
純函數(Pure Function)是這樣一種函數——輸入輸出數據流全是顯式(Explicit)的。
顯式(Explicit)的意思是,函數與外界交換數據只有一個唯一渠道——參數和返回值;函數從函數外部接受的所有輸入信息都通過參數傳遞到該函數內部;函數輸出到函數外部的所有信息都通過返回值傳遞到該函數外部。
非純函數
如果一個函數通過隱式(Implicit)方式,從外界獲取數據,或者向外部輸出數據,那麼,該函數就不是純函數,叫作非純函數(Impure Function)。
隱式(Implicit) 的意思是,函數通過參數和返回值以外的渠道,和外界進行數據交換。比如,讀取全局變數,修改全局變數,都叫作以隱式的方式和外界進行數據交換;比如,利用 I/O API(輸入輸出系統函數庫)讀取配置文件,或者輸出到文件,列印到螢幕,都叫做隱式的方式和外界進行數據交換。
引用透明
引用透明(Referential Transparent)的概念與函數的副作用相關,且受其影響。 如果程序中兩個相同值得表達式能在該程序的任何地方互相替換,而不影響程序的動作,那麼該程序就具有引用透明性。它的優點是比非引用透明的語言的語義更容 易理解,不那麼晦澀。純函數式語言沒有變數,所以它們都具有引用透明性。
範例(Java)
f(x) { return x + 1 }
f(x)函數就是純函數。
a = 0 q(x) { b = a }
q(x) 訪問了函數外部的變數。q(x)是非純函數。
p(x){ print “hello” }
p(x)通過I/O API輸出了一個字元串。p(x)是非純函數。
c(x) { data = readConfig() // 读取配置文件 }
c(x)通過I/O API讀取了配置文件。c(x)是非純函數。
純函數內部有隱式(Implicit)的數據流,這種情況叫做副作用(Side Effect)。我們可以把副作用想像為潛規則。上述的I/O,外部變數等,都可以歸為副作用。因此,純函數的定義也可以寫為,沒有副作用的函數,叫做純函數。
I/O API可以看作是一種特殊的全局變數。文件、螢幕、資料庫等輸入輸出結構可以看作是獨立於運行環境之外的系統外全局變數,而不是應用程序自己定義的全局變數。
特殊的函數副作用
上述只討論了一般的情況,還有一種特殊的情況,我們沒有討論。有些函數的參數是一種In/Out作用的參數,即函數可能改變參數裡面的內容,把一些信息通過輸入參數,夾帶到外界。這種情況,嚴格來說,也是副作用。也是非純函數。 比如下面的函數。
process(context) { a = context.getInfo() result = calculate( a ) context.setResult( result ) }
純函數的優點
純函數的好處主要有幾點: