【函式式】純函式與替代模型
純函式
一個函式在程式執行的過程中除了根據輸入引數給出運算結果之外沒有其他的副作用影響,我們可以把這類函式稱為“純函式”。純函式由於不依賴外部變數,使得給定函式輸入其返回結果永遠不變,比如整數的加法函式,它接收兩個整數值並返回一個整數值,對於給定的兩個整數值,它的返回值永遠是相同的整數值。
副作用
相對於非純函式,它們帶有副作用,這使得函式不僅簡單返回一個值,還做了其他事情:
- 修改了一個變數
- 直接修改了資料結構
- 設定了一個物件的成員
- 丟擲一個異常或以一個錯誤終止
- 列印到終端或讀取使用者的輸入
- 讀取或寫入一個檔案
- 在螢幕上繪畫
像是執行IO、處理錯誤、修改資料都屬於副作用,這些副作用可能導致我們編寫的程式難以測試,進而容易產生bug。遵循函數語言程式設計規範可以使程式設計更加模組化,由於純函式模組化的特性,使得程式容易被測試、複用、並行化、泛化以及推導
純函式的使用將資料的建立過程和處理過程分離,通過把副作用推導程式的外層,來轉換任何帶有副作用的函式。對於函式式程式設計師而言,程式的實現應該有一個純的核心和一層很薄的外圍來處理副作用。
引用透明
引用透明(referential transparency)的概念對純函式進行形式化,符合引用透明的表示式都可以由它的結果所取代,而不改變該程式的含義。當呼叫一個函式時所傳入的引數是引用透明的,冰鞋函式呼叫也是引用透明的,那麼這個函式式一個純函式。當傳入函式的引數也是一個純函式時,使得高階函式的組合也是引用透明的,這有利於構建更加複雜的邏輯,而本身程式的計算結果是可以進行推斷的,不用擔心環境對程式的影響。
對於程式p,如果它包含的表示式e滿足引用透明,所有的e都可以替換為它的運算結果而不會改變程式p的含義。假設存在一個函式f,若變大時f(x)對所有引用透明的表示式x也是引用透明的,那麼這個f是一個純函式。
替代模型
引用透明要求函式不論進行了任何操作都可以用它的返回值來代替。這種限制使得推導一個程式的求值變得簡單而自然,稱之為替代模型(substitution model)。如果表示式是引用透明的,可以想象計算過程就像在解代數方程。展開表示式的每一部分,使用指示物件替代變數,然後歸約到最簡單的形式。在這一過程中,每項都被等價值所替代,計算的過程就是一個又一個等價值所替代的過程。換句話說,引用透明使得程式具備了等式推理的能力。
替代模型更容易推理,因為對運算的影響純粹是區域性的(只對那些賦值表示式產生影響),不需要先在內心模擬一系列狀態的更新才理解這一段程式碼。只需要理解區域性的推理,不必費心地去跟蹤函式執行前後的狀態變化,只用簡單看一下函式的定義,把它替換成一個引數。
小結
這一小節,我們瞭解了純函式的一些基本概念和其帶來的好處。我們知道,純函式時模組化的、可組合的,因為它從“對結果做什麼”和“如果獲取輸入”中分離了計算本身的邏輯,就像一個黑盒子。對輸入的獲取只有一種方式:通過引數傳給函式。輸出也只是簡單地將計算結果返回。把這些關注點分離開,計算也更容易被複用。我們可以複用這些邏輯,而不必擔心輸入或輸出對整個上下文引起的副作用。