if

Scala里几乎所有的语句都是表达式。表达式就是可以求值的语句。在Scala里If也是表达式。而 If 表达式的值就是If语法块最后一个被执行的表达式的值。
在java里可能这么写代码:

int a = 0;
if(check()){
   a=1;
}else{
   a=2;
}

但是在Scala里你可以写成这样:

val a= if(check()) 1 else 2

循环控制语句

大部分的循环都是针对集合元素的,在函数式语言里,这些集合类型都提供给了我们强大的循环支持,比如foreach,map,filter,reduce,flatMap。你肯定听到过Hadoop的Map-Reduce计算框架。其实它的map和reduce就和我们这里集合的map,reduce原理是一样的。
在下边的例子里我们都以

val list = List(1,2,3,4)

为例。

foreach

foreach 方法需要你传入一个函数,这个函数的输入参数是集合的元素类型,没有输出,也就是Unit类型。我们现在来把list里的元素都打印一遍。

scala> list.foreach(println(_))
1
2
3
4

map

map 方法需要你传入一个函数,函数输入一个集合元素,你把它转化为另一个元素并返回。所有返回的元素构成一个新的集合。我们试着对所有元素都乘以2.

scala> list.map(_*2)
res54: List[Int] = List(2, 4, 6,8)

filter

filter 方法同样需要你传入一个函数,函数输入一个集合元素,你在函数内判断如果这个元素符合条件,则返回true。所有返回为true的元素会组成一个新的集合作为fitler方法的返回值。我们试着返回大于2的元素。

scala> list.filter(_>2)
res56: List[Int] = List(3)

reduce

reduce 方法有点特殊,它输入的是两个参数,输出一个值,类型都是集合元素类型。输入值有两个,一个值是上一次reduce计算的结果,一个是集合元素。中间结果初始化为集合里的一个元素。然后遍历整个集合,多次调用reduce方法。用来初始化中间结果的元素不参与遍历。我们看一下累加list里的元素的例子。

scala> list.reduce({(a,b)=>{println("acc: "+a);println("e: "+b);a+b}})
acc: 1
e: 2
acc: 3
e: 3
acc: 6
e: 4
res61: Int = 10

flatMap

flatMap 方法需要你传入一个函数,这个函数接收集合元素类型的参数,然后返回一个集合。集合的元素类型可以和原来集合元素类型不一致。我们试着把每一个元素转化成元素,元素+1的list,同时把新的list里的元素都变成String。
scala> list.flatMap({a=>List(a.toString,(a+1).toString)})
res65: List[String] = List(1, 2, 2, 3, 3, 4, 4, 5)
注意看返回结果的list类型已经是String。

说是循环控制语句,但是我们本身并没有控制循环。我们只是给循环提供了函数。这是Scala比Java优越的一个地方,既然程序可以帮我们做,我们为什么要自己操心呢?

while

while语句不是表达式,它不产生值。在函数式编程里尽量少的使用while。

scala> var a = 3
a: Int = 3

scala> while(a>0){
     | a=a-1
     | println(a)
     | }
2
1
0

它和我们在Java里使用while没有什么区别。

for

for语句在Scala里非常的强大。而且for可以作为一个表达式来使用。
首先我们看一个例子

scala> for(i<- List(1,2,3)){
     | println(i)
     | }
1
2
3

这和我们在java里的用法差不多,除了<- 符号,它表示对后边的集合元素遍历。接着我们看一点不一样的。

scala> for(i<- 1 to 10 if(i%2==0)){println(i)}
2
4
6
8
10

我们可以在for语句内通过if来过滤我们感兴趣的元素。对于多重循环,在Scala里也可以在一个for语句里完成。下边这个例子是找出10以内所有满足条件的包含两个元素的Tuple。条件是这个tuple第一个元素是偶数,第二个元素是比第一个数小的奇数。

scala> for(i<- 1 to 10 if(i%2==0);j<- 1 to i if(j%2==1)) println((i,j))
(2,1)
(4,1)
(4,3)
(6,1)
(6,3)
(6,5)
(8,1)
(8,3)
(8,5)
(8,7)
(10,1)
(10,3)
(10,5)
(10,7)
(10,9)

上边的例子1 to 10语句会产生一个从1到10的Range类型集合。我们通过两个<-来完成了双层循环。上边的语句为了方便阅读,也可以写成下边的样子:

scala> for{i<-1 to 10
     | if i%2==0
     | j<-1 to i
     | if j%2==1}
     | println((i,j))

可以看到for语句里可以用{}来代替(),替代后,语句之间的分号就可以省略了。
上边的例子,for语句都被作为一个命令式语句来使用,并不是一个表达式,因为它没有产生值。我们看一下如何用for语句来产生值。我们需要挑出从1到10里大于5的数:

scala> for (i<- 1 to 10 if i>5) yield i
res10: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 7, 8, 9, 10)

看到这里你可能觉得for语句有点复杂,不过还有一个知识点你要知道。它有一个很炫酷的名字叫做流间绑定量!我们的需求是从1到5,如果这个数的平方大于20,打印从20 到这个数的平方数。

scala> for{i<-1 to 5
     | square=i*i
     | j<-20 to square} println(j,square)
(20,25)
(21,25)
(22,25)
(23,25)
(24,25)
(25,25)

square这个val就是流间绑定量,但是它不用val关键字。这样在后边的语句里都可以使用这个常量了。

match case

match case在Scala里比Java有用的多,他可以做模式匹配,我们在后边会讲到。这里我们先了解一下match case的语法。同样match case是一个表达式,你可以将match case语句赋值给一个val。 我们看一下一个将英文转化为数字的例子。

scala> def getNumber(str:String):Int=
     | str match {
     | case "One" => 1
     | case "Two" => 2
     | case "Three" => 3
     | case _ => -1
     | }
getNumber: (str: String)Int
scala> getNumber("One")
res15: Int = 1

scala> getNumber("Four")
res16: Int = -1

真高兴Scala的match case 去掉了恼人的break,我敢说break给人带来的麻烦绝对比偶尔巧妙设计下它带来的好处多。这里好像没什么其他可说的,除了最后用_来对应java里switch语句里的default。其他看起来都比较直白。到我们学了模式匹配,你才能见识到match case的完全体。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: