在Scala中要定義函式,是使用def來定義,例如,以下是個求最大公因數的函式定義:
def gcd(m: Int, n: Int): Int = {
if(n == 0)
return m
else
return gcd(n, m % n)
}
在上例中,gcd是函式名稱,m與n為參數名稱,型態都是宣告為Int,雖然沒有標示出來,但在Scala中函式的參數都是val的,所以你不可以在函式中改變參數的值。括號接上一個冒號,而後的型態代表著傳回值型態,上例是以Int型態傳回函式的運算結果。
在Scala中,其實並不需要明確撰寫return(也不鼓勵),如果是個運算式,則以區塊最後一個語句的傳回值作為運算結果,如果是個函式,則以函式執行的最後一個語句作為傳回結果。所以上例可以撰寫為:
def gcd(m: Int, n: Int): Int = {
if(n == 0)
m
else
gcd(n, m % n)
}
如果是個單一語句,則大括號也可以省略,所以就可以寫成以下的形式:
def gcd(m: Int, n: Int): Int = if(n == 0) m else gcd(n, m % n)
在Scala中經常看到這樣的寫法。如果你沒有明確使用return,且函式並非遞迴的情況(遞迴函式必須明確地宣告傳回值型態),Scala在可以推斷出傳回值型態的情況下,你也可以省略傳回值型態的宣告,如上例中就只需在括號之後接上=即可。例如:
def max(m: Int, n: Int) = if (m > n) m else n
如果你的函式沒有傳回值,則無需使用等號,則可以宣告為Unit。例如:
def max(m: Int, n: Int): Unit = {
if (m > n)
println(m)
else
println(n)
}
Unit相當於其它語言(如C/C++、Java)void的作用,當一個操作不需傳回值時,Scala會傳回Unit,撰寫或顯示方式為()(說()是個值也可以,這是與void最大的差別,你可以指定一個變數為()),例如上例中,println()是在主控台顯示訊息,傳回值是Unit,而max函式定義傳回值也是Unit,你沒辦法拿Unit作什麼事,所以就相當於沒有傳回值。
當一個函式沒有傳回值時,你也可以省略Unit與=號宣告,例如:
def max(m: Int, n: Int) {
if (m > n)
println(m)
else
println(n)
}
一個沒有傳回值(傳回Unit)的函式,代表著這個函式會有邊際效用(Side effect),你執行了某個操作(可能也給了某些引數作為輸入),但不期待它有傳回值,那這個操作必然時某種形式對程式發生作用,也許是改變了程式中物件的狀態、改變了某些非函式中區塊變數的值、進行了輸入輸出操作(例如上例中,對主控台進行了輸出)等,不適當的邊際效用,容易對程式的維護造成負面影響。
Scala鼓勵你撰寫有傳回值的函式,這代表著你的函式會將輸入對應至輸出,這會使得你的函式易於測試。例如,若你如下撰寫程式,則你就只能使用眼睛從主控台觀察結果數字是否正確:
def gcd(m: Int, n: Int) {
if(n == 0)
println(m)
else gcd(n, m % n)
}
但若函式將輸入對應至輸出,則你可以使用斷言方式來檢測結果是否正確(斷言成立,什麼事都不會發生,斷言失敗,則會丟出java.lang.AssertionError):
def gcd(m: Int, n: Int): Int = if(n == 0) m else gcd(n, m % n)
assert(10 == gcd(10, 20))
在Scala中,函式是一級(First-class)物件,你可以用 函式常量(Function literal) 的方式來定義一個函式,執行時期將會為其產生函式值(Function value)。例如,上面的max函式,可以用以下的方式定義:
val max = (m: Int, n: Int) => if(m > n) m else n
println(max(10 ,20)) // 顯示 10
你使用=>定義函式常量,在上例中,=>左邊的(m: Int, n: Int)定義了函式的參數與類型,=>右邊則是函式本體,max的型態呢?實際上是(Int, Int) => Int,也就是實際上完整的宣告應該是:
val max: (Int, Int) => Int = (m: Int, n: Int) => if(m > n) m else n
一個實際的例子就是Array,它有個foreach方法接受一個函式常量,例如,若你想顯示使用者所輸入的命令列引數,則可以如下撰寫,而不用使用for運算式:
args.foreach((x: String) => println(x))
每一次從陣列中取出元素,就會呼叫foreach所傳入的函式值,在上例中,就是被x參考住,而後使用println()函式來顯示元素值。之後還會看到更多類似的應用,並會對一級函式作更多的介紹。