本頁使用了標題或全文手工轉換

函數副作用

維基百科,自由的百科全書
跳到: 導覽搜尋

在電腦科學中,函數副作用指當調用函數時,除了回傳函數值之外,還對主調用函數產生附加的影響。例如修改全域變數(函數外的變數)或修改參數。

函數副作用會給程式設計帶來不必要的麻煩,給程式帶來十分難以查找的錯誤,並降低程式的可讀性。嚴格的函數式語言要求函數必須無副作用。

下面是函數的副作用相關的幾個概念,純函數(Pure Function)、非純函數(Impure Function)、引用透明(Referential Transparent)。

純函數[編輯]

純函數(Pure Function)——輸入輸出資料串流全是顯式(Explicit)的。

顯式(Explicit)的意思是,函數與外界交換資料只有一個唯一渠道——參數和回傳值;函數從函數外部接受的所有輸入資訊都通過參數傳遞到該函數內部;函數輸出到函數外部的所有資訊都通過回傳值傳遞到該函數外部。

非純函數[編輯]

如果一個函數通過隱式(Implicit)方式,從外界獲取資料,或者向外部輸出資料,那麼,該函數就不是純函數,叫作非純函數(Impure Function)。

隱式(Implicit)的意思是,函數通過參數和回傳值以外的渠道,和外界進行資料交換。比如,讀取全域變數,修改全域變數,都叫作以隱式的方式和外界進行資料交換;比如,利用 I/O API(輸入輸出系統函式庫)讀取設定文件,或者輸出到文件,打印到螢幕,都叫做隱式的方式和外界進行資料交換。

引用透明[編輯]

引用透明(Referential Transparent)的概念與函數的副作用相關,且受其影響。如果程式中任意兩處具有相同輸入值的函數調用能夠互相置換,而不影響程式的動作,那麼該程式就具有引用透明性。它的優點是比非引用透明的語言的語義更容易理解,不那麼晦澀。純函數式語言沒有變數,所以它們都具有引用透明性。

範例[編輯]

 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) { 
   // 假設readConfig()函數為I/O API的函數
   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)
 }

純函數的優點[編輯]

純函數的好處主要有幾點:

  • 無狀態。執行緒安全。不需要執行緒同步。
  • 純函數相互調用組裝起來的函數,還是純函數
  • 應用程式或者運行環境(Runtime)可以對純函數的運算結果進行快取,運算加快速度。