《程式設計導論(Java)·3.1.2 方法》之 副作用
4. 副作用
在一些語言如Pascal中,子程式被分成兩種:函式和過程。雖然Java沒有強制性地要求將方法區分為命令和函式,然而這種區別對於良好地設計程式有很大的幫助[1]。
首先說明一個概念:副作用(side effect)。副作用一般是針對操作(表示式)而言的,一個操作/表示式有“副作用”是指在對該表示式求值時,會改變程式的一個或多個數據,以致再次對該表示式求值時,可能會得出不同的結果。事實上,Java的4種表示式語句如賦值、自增自減、方法呼叫、物件建立都可能帶來副作用。
這裡討論方法的副作用。一個方法的執行,如果在返回一個值之外還導致某些外部“狀態”發生變化,則稱該方法產生了副作用。這裡所謂“狀態”發生變化,可以是例項域或靜態變數被修改、方法的實參被修改(Java 中不會出現這種情況。但是實參為引用時,其指向的物件可能被修改從而產生副作用)、將資料傳遞給顯示器、印表機或存入檔案中等等。
當然,方法內部的表示式也會出現副作用,如果它僅僅影響區域性變數而不影響外部狀態,則方法沒有副作用。基於副作用概念,定義兩個術語:
² 有返回值而且沒有副作用的方法稱為函式(function)。
² 沒有返回值的方法必然有副作用,除非它的方法體是空的或者方法沒有意義。所以,沒有返回值的方法、有返回值但有副作用的方法稱為過程(procedure)或命令(command)。簡言之,有副作用的方法稱為過程。
如此嚴格地定義出函式的概念,是因為函式使得系統的狀態穩定,函式的行為容易預測。更進一步,如果函式是純粹的函式(pure function,純函式)——它的輸出值依靠和僅僅依靠其輸入、對於相同的輸入總是返回相同的值,(由於純函式的純粹和無副作用)對純函式的呼叫就能夠被一個值取代(或者說,將方法視為一個值),
例程 3‑3純函式 package semantics.method; public class SideEffectsDemo{ private static int x = 0; //純函式(pure function) public static int times(int i,int j){ return i * j ; } //非純函式 public static int m(int i,int j){ return i * j +x; } }
在函數語言程式設計語言(functionalprogramming language)如Haskell[2]中,尤其強調避免副作用。當然了,完全不產生副作用的程式語言是沒有任何用處的,例如資料顯示和存入檔案等等副作用都是必要的,強調無副作用的Haskell 語言,使用一種技術將它們分離出來,用一種安全的方式單獨執行。
Java是命令式面嚮物件語言,但能夠借鑑函式式語言的優點,也期待Java中加入重要的函式式語言的特性。隨著Java 8的釋出,引入的λ表示式(Lambda Expressions)表明,Java開始大力引入函式式語言的特性。(They enable you to treat functionality as a method argument, or code as data.)
練習3-5:何謂方法、函式、純函式? 解釋副作用的含義。 |
練習3-6:實現方法,求f(x)=x^3 + 3x+1。注:書中x^3表示x*x*x. |
[1] 參考RichardMitchell,Jim Mackim著,孟巖譯,Design by Contract原則與實踐。
[2] http://www.haskell.org/learning.html