2014年1月26日 星期日

Scala Basic1, 變數

前言
       如果你本身是一個OOP Programmer, 透過OOP的概念來進入Scala是一個很好的開始, 尤其當你又是JAVA Programmer, 那麼該是時候來藉由Scala來進化到下一個階段:有生產力且有效率的Program. Scala並非一個全新的語言, 當你遇到學習瓶頸的時候, 可以思考看看這個語法特性在JAVA or其他語言是長怎麼樣, Scala又如何做新的詮釋? 很多東西在Scala表面上看似新的, 但Scala只是基於過去的功能做更進化且更好的應用.

Variable:是變數也全都是物件


在Scala中,一般常見的基本型態都可以使用, ex: Byte, Short, Int, Float, Double, Boolean, and Char這些的在記憶體的Size都跟你所知道的沒有太多差別, 唯一你該知道的是, 在Scala中所有東西都是物件, Scala是包含OOP特性的語言, 也因此就貫徹始終的善用物件, 你會注意到的是所有的基本型態在Scala裡使用第一個字母是大寫, 這跟你在JAVA裡面有所不一樣
//scala code
val i : Int = 10
上面範例是宣告一個Integer變數, 也是物件, 在JAVA中 Integer是基本型態不是一個物件, 也因此宣告的時候會是 int i = 10; 第一個字母是小寫, 讓你知道他是一個keyword去表示基本型態, 而如果是大寫的話就是物件, 但這些在Scala不會區別了, 基本型態一切都是物件;第一個字母都是大寫

Type Inference:類型推斷


Scala有一個很強的功能就是Type inference, 你不必宣告變數的型態, Scala compiler會自動幫你推斷該變數型態

自動推斷數字型態

//Numeric literal
val i = 10 
val hexa = 0xFF1C
val octa = 023
val l = 1000L
val d = 0.0
val f = 0.0f
val exponent = 1e30
  • 變數 i 就會因為給他數字而被推斷是Int 
  • Scala在數字型態也可接受多樣化的格式; hexa變數因為你數字前面給0x, 所以被推斷是16進位格式, 最後被存成Int 的65308
  • octa因為數字前面是0, 所以被推斷成8進位, 最後會被存成Int 的19
  • 要放在Long的物件裡面的話, 就必須在數字最後面加個L, Compiler就會推斷這是一個Long integer
  • 小數的浮點運算 : 數字有小數點就會被推斷成Double
  • 小數點數字最後加上f, 會被推斷成Float
  • Scala也可以判斷科學記數法, 在數字前面加上1e,  exponent變數就存成 1.0E30


自動推斷字元型態


//Character literal
val capB1 = '\102'
val capB2 = 'B'
val \u0061\u006e\u0073 = 42
val ? = scala.math.Pi
//String literal
val bookName = "Scala in \"Action\""
//String interpolation
val name = "Nilanjan"
val sentence = s"My name $name"
  • Scala是用Unicode編碼, 因此可以用Unicode 來表示字元, 因此capB1變數的 \102 會被轉成Unicode的 B, 而capB1也會被推斷是Char
  • capB2因為你給他一個字元 'B' , 因此被推斷是Char
  • Scala變數名稱也可以用Unicode代碼去表示, 比如\u0061\u006e\u0073變數名稱會轉換成相對應的Unicode
  • 任何Unicode符號都可以當作變數名稱, 所以像 ? 也可以被宣告成一個變數
  • 字串變數是透過 "" 雙引號來判斷, 如果要在字串中顯示雙引號要用 \ 符號, 可以參照 bookName ,  bookName 會被判斷是字串, 值是 Scala in "Action"
  • Scala字串也有差值功能, 在一個字串裡面插入字串, sentence字串變數裡面就透過$符號來插入name的字串, 最後 sentence字串會是 My name Nilanjan


自動推斷XML格式


val book = 
           Scala in Action
           Nilanjan Raychaudhuri
           
Scala比較特殊的地方就是可以解析XML格式的Code, 這是一個很棒的功能, 尤其當你在處理資料型態轉換, 正規化, Web service等等, 會很有幫助, Scala會把你的XML轉換到scala.xml.Elem 物件裡面, 所以book 是scala.xml.Elem型態, 表達的是:
book: scala.xml.Elem = 
    
       Scala in Action
       Nilanjan Raychaudhuri
  
Scala不只可以解析XML格式, 還可以允許內插入Scala code 以及變數, 只要透過 curly brace {}:大括號在XML的標籤裡面,
val message = "I didn't know xml could be so much useful"
val code = “1”
val alert = 
            {message} 
            {new java.util.Date()}
            
上述code, 就是把message變數放入XML的message標籤, 並且把message標籤的參數priority,設為code變數值, date標籤也放入java.util.Date()物件, 而這一切只是透過 curly brace {}:大括號, 最後輸出結果如下
alert: scala.xml.Elem = 

       
            I didn't know xml could be so much fun
        
       Fri Feb 19 19:18:08 EST 2010
 

Defining variables:開始Coding的第一步

在上面的變數介紹和類型推斷, 你已經看過不少變數宣告的例子, 很顯然每一個變數的宣告必然前面都會放 val 或者 var, 這兩個Keyword是你踏入Scala第一個要牢記的特性, 就是變數的性質, 是Mutable(可變動) or Immutable(不可變動)
  • val代表的是single assignment variable,表明變數就是一個value, 只有宣告的時候可以給初始value, 往後執行時期都不能再去改變該值(很類似JAVA的 final 變數)
  • 相反的, var代表就是variable, 讓你的變數值可以任意更動(就跟你過去Programming的變數一樣)
為什麼Scala特地發明val 關鍵字, 來強制變數都不能再被改變? 主要目的是為了Parallel programming平行化程式, 相關理由在前言也充分解釋 val 變數的重要性, 儘管不能任意更動變數很不方便, 這一步讓後面的Programming開始變得陌生, 也要強迫試著從宣告 val 變數開始寫程式,

當你的的程式企圖修改一個 val 變數的時候, 會出現Compile error,
val immutable = 99
immutable = 100
就會出現錯誤代碼 : 5: error: reassignment to val constant = 100 

Keyword :冒號, 用來指定型態

:冒號在Scala有著特殊意義, 就是指定類型, 在Scala變數宣告裡面, 變數名稱後面接上:,再接上的就是該變數型態, 這在變數宣告以及函式定義都會看到, 儘管透過Type inference你可以不必常常指定變數型態, 但是明確的定義型態也有助於code可讀性


_(placeholder, 底線,佔位自元), 用來當作變數的初始值
在Scala中變數宣告的時候都要給其初始值, 變數宣告後面不能空白著, 如果當你在宣告var Mutable變數時候不知道放甚麼, 可以先放底線 _  當作Default value
var willKnowLater:String = _
上例的的結果是 willKnowLater: String = null, 因為你指定為String型態, 而String的Default value是null, 你可以去試試看其他型態, 看看Default value是甚麼, 唯一你可以宣告變數卻不必給它初始值的地方就是class definition.

Keyword:lazy ,  更加善用CPU資源, 真正的動態分配

在Scala預設的情況下, 變數的Value是在宣告時期就計算好的, 可是當你宣告一些變數的值, 它的計算時間是 time-consuming (非常耗時)的, 整個程式就會卡在宣告時期, 程式跑半天才有畫面, 就好比Windows開機等很久一樣, 最好的解決方式應該是當你真的需要用該變數的時候, 才去真正計算它的值,  這就要在變數宣告的最前面加上Keyword lazy

lazy val forLater = TimeConsumingOperation()
//forLater: Unit = <lazy>
forLater變數就是接收非常耗時函式(TimeConsumingOperation)的Result, 因為加上的lazy Keyword, TimeConsumingOperation()還不會執行, 而forLater還沒有拿到真正的Result, 只是給它一個初始<lazy>值, 當你真正存取使用到forLater時候 TimeConsumingOperation才會執行並且拿到Result

var a = 1    //a: Int = 1
lazy val b = a + 1  //b: Int = <lazy>
a = 5        //a: Int = 5
println(b)  //6
在這個例子可以更顯然了解lazy怎麼運作的, 因為變數 b 是 lazy, 所以當宣告時期的值還只是<lazy>, 而往後再改變 a 的值, 然後存取 b 去顯示出來的時候會得到 6, 這是因為 b 的值 a+1是在 a = 5 之後才計算的, 如果變數 b 不是 lazy, 而最後印出來的反而會是 2 , 因為 b 的值 a+1 是在 a=5之前就先計算好了, a 的改變已經來不及影響 b 了

lazy Keyword 只允許給val 變數使用,  var要使用的話會出現Compiler error

在Scala中, 每行指令最後都不需要加上 ;分號表示結尾, 這跟以往在其他語言比如JAVA很不一樣, Scala訴求寫得更少, 做得更多, Scala code相對JAVA會少很多, 當然如果你還是要加;分號, Compiler還是會讓你過的

 Pattern  declaration 樣板模式宣告變數

在變數宣告的左邊, 變數名稱的定義上面, 除了定義一個名稱給變數之外, 也可以用Pattern來定義, 這部分有點複雜, 簡單介紹給一個觀念, 往後會有更仔細的說明
val first :: rest = List(1, 2, 3)
first: Int = 1
rest: List[Int] = List(2, 3) 
first :: rest變數就不是一個完整變數名稱, 而是一個List pattern, List是一個Scala immutable物件, 用來表示集合串列, List[String] 就是一系列String物件的串列(跟JAVA List類似), 變數first :: rest 就是該串列的 Pattern, 一個 List 串列可以表示成 頭(Head) + 尾巴(Tail) 的Pattern, Head表示串列的第一個元素, Tail表示除了頭以外剩下來的元素串列, 所以 Pattern first :: rest就可以解析List(1 ,2, 3) 變成Pattern List(1) + List(2, 3), 其中first 就對應到 List(1), rest 對應到 List(2, 3),  first 與 rest 中間的::兩個冒號是List 物件的method, 用來表示串接, 表示把 first 與 rest這兩個子 List 串接起來, 這種Pattern 的對應, 在Scala上是一個很強大的功能Pattern match, 每種物件可能都有其 Pattern可以用來解析和應用, 上述的List(1, 2, 3) 用Pattern來對應到變數後, 就可以直接用 first 存取 1, rest 存取 2, 3 ;  Pattern match更多的後面會更進一步說明


在Scala接觸到第一個最特別的非 val 變數為主, 儘管還不習慣且不明白到底val 的好處, 還是牢記在心底並試著宣告 val 變數 , 除非有必要才使用var

繼續閱讀 Scala Basic2, 函式

沒有留言:

張貼留言