2016年4月18日 星期一

[Scala 小學堂] Scala Gossic : 起步走 - 函式、類別 (簡單的函式)

轉載自 這裡 
前言 : 
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. 從一些簡單的語法開始,了解 Scala 的許多特性,所以就算是起步走,東西也夠多了. 簡單的小程式可以使用 scala 指令,編譯的事就交給 scalac,編譯想快點就用 fsc. (Scala 官網), 在簡單的自定義後,將介紹更多的花樣. 

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

補充說明 : 
Tutorialspoint - Scala Functions

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...