Scala的for表达式是枚举操作的“瑞士军刀”,它可以让你用不同的方式把若干简单的成分组合起来以表达各种各样的枚举。
枚举集合类
For能做的最简单的事情就是把集合中所有元素都枚举一遍。如例:
package scalaTest object Test4 { def main(args:Array[String]) { val fileHere = (new java.io.File(".")).listFiles for(file <- fileHere) println(file) } }
通过使用被称为发生器的语法“file <- filesHere”,我们遍历了filesHere的元素。每一次枚举,名为file的新的val就被元素值初始化。编译器能够推断出file的类型是File,因为fileHere是Array[File]。对于每一次枚举,for表达式的函数体println(file),将被执行一次。由于File的toString方法生成文件或目录的名称,因此当前目录的所有文件和目录的名称都会被打印出来。
For表达式语法对任何种类的集合类都有效,而不只是数组。如例,你可以使用类似“1 to 5”这样的语法很方便地创建Range(这个后面再讲解),然后用for做枚举。
package scalaTest object Test4 { def main(args:Array[String]) { for(i <- 1 to 9) println("Iteration " + i) } }
如果不想包括被枚举的Range的上边界,还可以用until替代to,如例:
package scalaTest object Test4 { def main(args:Array[String]) { for(i <- 1 until 9) println("Iteration " + i) //注意这里 } }
过滤
有时候你并不想枚举集合的全部元素,而只是想过滤出某个子集。这可以通过在for表达式的括号中添加过滤器(if子句)实现。如例:
package scalaTest object Test4 { def main(args:Array[String]) { for(i <- 1 until 9 if i % 2 == 0) println("Iteration " + i) } }
或者也可以采用如下写法:
package scalaTest object Test4 { def main(args:Array[String]) { for(i <- 1 until 9) if(i % 2 == 0) println("Iteration " + i) } }
这段代码对于指令式背景的程序员来说看上去更熟悉一些。然而指令式的格式只是可选的实现方法之一。For表达式之所以被称为“表达式”是因为它能产生有意义的值,其类型取决于for表达式<-子句的集合。需要的话,你可以包含更多的过滤器。只要不断添加if子句即可。如例:
package scalaTest object Test4 { def main(args:Array[String]){ for( i <- 1 until 9 if i % 2 == 0; //注意这里要分号 if i % 4 == 0 ) println("Iteration " + i) } }
注意:如果在发生器中加入超过一个过滤器,if子句必须用分号分隔(注:在scala2.10.7版本下好像不用分号了!!!)。
嵌套枚举
如果加入多个 <-子句,你就得到了嵌套的“循环”。如下例:
package scalaTest object Test4 { def main(args:Array[String]){ val arr = Array(Array(1,2,3,4,5),Array(11,12,13,14,15)) for( ar <- arr if ar(0) > 10; //注意这里要加分号 a <- ar if a % 2 == 0 )println("Iteration " + a) } }
注意:嵌套枚举之间也必须用分号分隔。如果愿意的话,你可以使用花括号代替小括号包裹发生器和过滤器。你用花括号的好处是可以省略使用小括号时必须加的分号(当然不去掉分号也是可以的);如例:
package scalaTest object Test4 { def main(args:Array[String]){ val arr = Array(Array(1,2,3,4,5),Array(11,12,13,14,15)) for{ //注意这里,是大括号哦 ar <- arr if ar(0) > 10 a <- ar if a % 2 == 0 }println("Iteration " + a) } }
流间变量绑定
package scalaTest object Test4 { def main(args:Array[String]){ val arr = Array(Array(" a","b"," c "),Array("D"," E","F ")) for{ ar <- arr if ar(0).trim == "a" a <- ar if a.trim == "c" }println(a.trim) } }
请注意前面的代码段中重复出现了表达式a.trim,这是个不可忽略的计算,因此你或许希望只算一遍。这可以通过用等号(=)把结果绑定到新变量实现。绑定的变量被当作val引入和使用,不过不带关键字val,如例:
package scalaTest object Test4{ def main(args:Array[String]){ val arr = Array(Array("a","b "," c "),Array("D"," E","F ")) for{ ar <- arr if ar(0).trim == "a" a <- ar //注意,这里要么换行,要么加分号 k = a.trim if k == "c" }println(k) } }
不换行,加分号的例子:
package scalaTest object Test4 { def main(args:Array[String]) { val arr = Array(Array(" a","b "," c "),Array("D"," E","F ")) for{ ar <- arr if ar(0).trim == "a" a <- ar;k = a.trim if k == "c" //这里加了分号 }println(k) } }
代码中,名为k的变量被从半路引入for表达式,并被初始化为a.trim的结果值。于是之后的for表达式在两个地方使用了新的变量,一次在if中,另一次在println中。
制造新集合
到现在为止所有的例子都只是对枚举值进行操作然后就释放,除此之外,你还可以创建一个值去记住每一次的迭代,只要在for表达式之后加上关键字yield。如例:
package scalaTest object Test4 { def main(args:Array[String]){ val arr = Array(Array(" a","b "," c "),Array("D"," E","F ")) //注意这里 var arr2 = for{ ar <- arr if ar(0).trim == "a" a <- ar;k = a.trim if k == "c" || k == "b" }yield k for(s <- arr2) println("子集合元素: " + s) } }
又例:
package scalaTest object Test4{ def main(args:Array[String]){ val arr = Array(Array(" a","b "," c "),Array("D"," E","F ")) def fuOne(arr:Array[Array[String]]) = for{ ar <- arr if ar(0).trim == "a" a <- ar;k = a.trim if k== "c" || k == "b" }yield k for(s <- funOne(arr)) println("子集合元素: " + s) } }
For表达式在每次执行的时候都会产生一个新值,本例中是k。当for表达式完成的时候,结果将是包含了所有产生值的集合对象。对象的类型基于枚举子句处理的集合类型(上例中就是Array[String],这里是一维的)。
另外,请注意放置yield关键字的地方。对于for-yield表达式的语法是这样的 :for {子句} yield {循环体},如例:
package scalaTest object Test4 { def main(args:Array[String]){ val arr = Array(Array(" a","b "," c "),Array("D"," E","F ")) def funOne(arr:Array[Array[String]]) = for{ ar <- arr if ar(0).trim == "a" a <- ar;k = a.trim if k == "c" || k == "b" }yield{ println("~~~~~" + k) k } } }
yield在整个循环体之后。即使循环体是被花括号包围的代码块,也一定要把yield放在左花括号之后。
截止目前,你已经看过了scala的for表达式的所有主要特征,不过这一段过得实在是快了一些,我们后面会再详细介绍。