在Programming language裡面, 迴圈的設計能夠重複的執行某些工作, 藉此機器的運作才得以日復一日規律的執行, 如同世界運轉一般, 在Scala中迴圈 while 如JAVA一般沒甚麼不同, 可是For發展出更有彈性的應用, 尤其遇上了Immutable 變數的時候, 不能用過去 int i 當作迴圈次數索引, 需要有更方便的架構來幫助軟體運行
For-comprehensions
- 控制式用來定義for 重複執行相關的條件, 比如重複次數, 重複終點等
- Expression block就是for 要重複執行的任務本體
先把目光放在Scala中最常用到 for 的 pattern, Scala在資料結構上有很多種類的Collection可以善用, 比如上一章節的List, 透過 for loop 去循序存取整個Collection, 就得藉由Generator 的機制, 先看一個顯示以 .scala 為附檔名的檔案範例
val files = new java.io.File(".").listFiles for(file <- files) { val filename = file.getName if(fileName.endsWith(".scala")) println(file) }
Generator
比較會引人好奇的code 就是 file <- files, 在Scala中它就是Generator, 主要目的就是去循序存取整個Collection, 所以整個for loop會執行的次數就是 files這個Collection的長度, Generator的格式中, <-的右邊是Collection 在範例中是 files, 在左邊的是會把在該次迴圈中從 files取出來的元素, 其實上面的code在JAVA中會如下for(File file: files) { String filename = file.getName(); if(filename.endsWith(".scala")) System.out.println(file); }你會發現在Scala版本中的你不需要去指定從Collection中取出來的元素類型, 這也是受惠於Type inference的作用, Scala compiler會自動幫你辨別類型, 除了透過Generator之外, 你也可以這樣撰寫
for( file <- files; fileName = file.getName; if(fileName.endsWith(".scala")) ) println(file)
for-loop format |
An enumerator is either a generator which introduces new variables, or it is a filter.
- Generator就如同前面所述, 用來循序存取Collection的元素
- Filter的作用就如同上面例子的if ( fileName.endsWith(".scala") ) 的功能, 用來過濾你要的 .scala 檔案(也是guards)
- definitions就如同 fileName = file.getName; 你可以自己定義一些for-loop控制式所需的元件
- 再簡化, 上例中 println(file) 還是 Expression block, 只是因為簡化成只有一行code, 大括號{ }都省略了
巢狀迴圈的簡化
當你有個多維陣列, 你要一個個存取出陣列中所有的值, 最基本的做法就是巢狀迴圈, for-loop中再巢狀多個for-loop, 可是在Scala for-loop的format中, 你可放Generator來循序存取Collection中的值, 如果同時放多個Generator , 也是可行的, 並且有巢狀的效果, 看看以下例子var a = Array(Array(1, 2, 3), Array(4, 5, 6), Array(7, 8, 9)) for( i<-a ; k <- i) print(k+" ") //1 2 3 4 5 6 7 8 9第一個Generator i <- a 把Array第一維度的元素Array(1 ,2, 3) , Array(4 ,5, 6) , Array(7 ,8, 9) 存取出來放到 i , 然後第二個Generator k <- i 把 第二維度的Array中的元素存取循序出來, 多個Generator同時放在同一個for 控制式裡面, 就會有巢狀迴圈效果
for-comprehensions of functional form
事實上, 在Scala中for-loop是有兩種類型- Imperative form, 就是會幫你重複執行你所指定的任務, 但不回傳任何結果
- Function form, 在Function form中比較趨向於做數值運算相關的任務, 並最後回傳結果
比如去計算一個九九乘法所有可能結果,並且用字串存成一個九九乘法表
val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9) val b = List(1, 2, 3, 4, 5, 6, 7, 8, 9) val c = for( f <- a, s <- b) yield f + " * " + s + " = " + f*s //c: List[String] = List(1 * 1 = 1, 1 * 2 = 2...............) c.foreach(println(_))在這邊你會發現for的 expression block會變成由一個 yield keyword開頭, yield keyword 會把 expression block的每一圈結果集合成一個List, 並回傳出來, 這樣的把結果存起來, 使其更有重複使用性與相容性, 這樣的點子也是來自於Functional Programming的概念, 再一個例子, 把function form的結果作更多應用
val xmlNode =mkString是List的一個method, 它會把List中每一個元素都串接在一起成一個字串, 而在這個字串中每一個元素中間相隔的符號就是你傳入mkString的參數, 在上面例子是 comma 逗號{c.mkString(",")} xmlNode: scala.xml.Elem =1 * 1 = 1, 1 * 2 = 2...............
如果在yield 中放的是println 呢? 你該記得的是任何事物在Scala中都是Expression, 並且回傳一個結果, 凡事沒有例外, println也是有回傳值, println的回傳值是Unit, 就如同JAVA中的void一般, 因此如果放println 在yield中你只會得到一個Unit 的List,
val c = for( f <- a, s <- b) yield println(f + " * " + s + " = " + f*s) //c : List[Unit] = List((), (), (), (), (), (), (), (), ()..................)
Scala的 for提供了更有活性的應用, 讓你的程式可以簡單的存取各種資料結構中並且運作, 更進一步的應用如Functional data type會在後面詳述
下一章節Scala basic5, Pattern matching
沒有留言:
張貼留言