第 3 章 Scala控制结构 Scala程序中的语句默认是按照书写顺序依次被执行的,这样的语句结构就是顺 序结构。仅有顺序结构还是不够的,因为有时人们需要根据特定的情况有选择地执 行某些语句,这时就需要一种选择结构的语句。另外,有时人们还需要在给定条件 下重复执行某些语句,这时就需要循环结构的语句。有了顺序、选择和循环这三种 基本的结构,就能构建复杂的程序了。本章主要介绍布尔表达式、选择结构、条件表 达式、while循环、for循环、for推导式、块表达式赋值、循环中的break和continue等。 .. 3.1 布尔表达式 选择结构和循环结构都需要使用布尔表达式作为选择和循环的条件。布尔 表达式是由表达式、关系运算符和逻辑运算符按一定的语法规则组成的式子。前 文已介绍过Scala的关系运算符有<(小于)、<=(小于或等于)、==(等于)、> (大于)、>=(大于或等于)、! =(不等于),逻辑运算符有&&(逻辑与)、||(逻辑 或)、! (逻辑非)。 由于布尔数据类型(Boolean)的数据值只有两个(true和false),所以一个布 尔类型的变量的值只能是true或false,布尔表达式的值也只能取true或false。 布尔表达式举例如下。 scala>var a =true a: Boolean =true scala>var b =false b: Boolean =false scala>a&&b res10: Boolean =false scala>a||b res11: Boolean =true .. 3.2 选择结构 3.2.1 单向if选择语句 Scala的if语句与其他语言的if语句相似,可以检测条件并根据其是否为真 来决定是否执行对应的分支。if语句由布尔表达式及之后的语句块组成,具体语 第3章 Scala控制结构 25 法格式如下所示。 if(布尔表达式) { 语句块 //如果布尔表达式为true 则执行该语句块 } 举例如下所示。 scala>:paste //进入代码块编写模式(paste 模式) // Entering paste mode (ctrl-D to finish) var x =10 if( x <20 ){ println("x <20"); } // Exiting paste mode, now interpreting 代码编写完成后可按Ctrl+D组合键退出paste模式并执行输入的代码,执行结果如下 所示。 x <20 3.2.2 双向if…else选择语句 if…else语句的语法格式如下所示: if(布尔表达式){ 语句块1 //如果布尔表达式为true 则执行语句块1 }else{ 语句块2 //如果布尔表达式为false 则执行语句块2 } 举例如下所示。 scala>:paste // Entering paste mode (ctrl-D to finish) var x =30; if (x <20 ){ println("x 小于20"); } else{ println("x 大于或等于20"); } // Exiting paste mode, now interpreting 退出paste模式并执行代码,结果如下所示。 x 大于20 x: Int =30 3.2.3 嵌套if…else选择语句 if语句后可以紧跟elseif…else语句,在多个条件下判断语句的执行情况。嵌套if… 26 Spark大数据处理技术与实战(Scala版·微课版) else语法格式如下。 if(布尔表达式1){ 语句块1 //如果布尔表达式1 为true 则执行语句块1 }else if(布尔表达式2){ 语句块2 //如果布尔表达式2 为true 则执行语句块2 }else if(布尔表达式3){ 语句块3 //如果布尔表达式3 为true 则执行语句块3 }else { 语句块4 //如果以上条件都为false 执行语句块4 } .. 3.3 条件表达式 Scala的if…else语法结构和Java一样。不过,在Scala中if…else表达式有值,这个值 就是跟在if或else之后的表达式的值。在Scala中,条件表达式的用法举例如下所示。 if (x >0) 1 else -1 上述条件表达式的值是1或-1,具体是哪一个取决于当前程序流中x的值。用户可以 将条件表达式赋值给变量,示例如下。 val s =if (x >0) 1 else -1 这与如下语句的效果一样。 if (x >0) s =1 else s =-1 不过,两者相较而言第一种写法更好,因为它可以用来初始化一个常量,而在第二种写 法当中,s必须是变量,如下所示。 scala>val x =3 x: Int =3 scala>val y =if ( x >1 ) 1 else -1 y: Int =1 .. 3.4 while循环 Scala拥有与Java相同的while循环和do…while循环。while循环的语法格式如下 所示。 while(布尔表达式){ 循环体 } while循环举例如下所示。 scala>:paste // Entering paste mode (ctrl-D to finish) var i:Int =0 //定义变量 while (i <5){ 第3章 Scala控制结构 27 print("hello" +i+" ") i +=1 } // Exiting paste mode, now interpreting 代码编写完成后按Ctrl+D 组合键退出paste模式并执行输入的代码,执行结果如下 所示。 hello0 hello1 hello2 hello3 hello4 do…while循环的用法也沿袭Java,如下所示。 do{ 循环体 }w hile(条件语句) .. 3.5 for循环 Scala的for循环的语法格式如下。 for(控制变量<-可遍历序列){ 循环体} 这里的“<-”是for循环的组成部分。for循环是一种遍历型的循环,它会依次遍历有 序集合中的元素,每遍历一个元素就执行一次循环体,遍历完所有元素之后便退出循环。这 种有序集合可以是数组、列表、字符串、元组、集、映射等。for循环的使用方式主要有以下 几种。 (1)使用for(x<-Range)的方式,用法如下。 for(x <-Range ){ 循环体} 其中,Range可以是一个数字区间,如itoj,或者iuntilj。左箭头“<-”用于为变量x 赋值。itoj表示的区间是[i,j],iuntilj表示的区间是[i,j),两种形式举例如下。 scala>for (i <-1 to 3) {println(s"Day $i:")} Day 1: Day 2: Day 3: scala>for (i <-1 until 10) printf("%d ",i) 1 2 3 4 5 6 7 8 9 (2)使用分号“;”设置多个区间,它将迭代给定区间所有可能的值,示例如下。 scala>for( a <-1 to 2; b <-1 to 2){ println( "a: " +a +" b: " +b) } 运行上述代码得到的输出结果如下。 a: 1 b: 1 28 Spark大数据处理技术与实战(Scala版·微课版) a: 1 b: 2 a: 2 b: 1 a: 2 b: 2 scala>for( i <-1 to 5 if i%2 !=0;j <-1 to 5 if j%2 ==0 ) println( i +" * " +j +" =" +i*j) 1 * 2 =2 1 * 4 =4 3 * 2 =6 3 * 4 =12 5 * 2 =10 5 * 4 =20 (3)用for循环遍历数组、列表和集合,示例如下。 scala>val list1=List(3,5,2,1,7) //创建列表 list1: List[Int]=List(3, 5, 2, 1, 7) scala>for(x <-list1){print(" "+x)} 3 5 2 1 7 (4)在for循环中使用过滤器,示例如下。 scala>for(x <-list1 if x%2==1){print(" "+x)} 3 5 1 7 scala>var bArray=Array("宋爱梅","王志芳","李涛","贾燕青","刘振杰","刘涛") scala>for(file <-bArray if file.endsWith("涛")){println(file)} 李涛 刘涛 用户可以在for循环中引入已在循环中使用的变量,示例如下。 scala>for(i<-1 to 3; from =4-i; j<-from to 4) print((10*i+j).toString+" ") 13 14 22 23 24 31 32 33 34 【例3-1】 通过索引使用for循环和until关键字遍历数组。 下面需要在交互模式下使用代码块一次运行多条语句。先输入“:paste”并回车进入 paste模式,然后粘贴(写入)代码块。 scala>:paste //进入粘贴模式 // Entering paste mode (ctrl-D to finish) var a =Array("hello","Scala") for (i <-0 until a.length) println(a(i)) // Exiting paste mode, now interpreting 之后按Ctrl+D组合键结束输入并运行代码块。 hello Scala 如果想要在遍历元素时隔一个元素执行一下处理操作,则可以采用如下方式。 scala>val b=Array(1,2,3,4,5,6) 第3章 Scala控制结构 29 b: Array[Int]=Array(1, 2, 3, 4, 5, 6) scala>for (i <-0 until (b.length,2)) | printf("%d ",i) 0 2 4 从数组的尾端开始倒序遍历,语法格式如下。 for(i <-(0 until b.length).reverse) 循环体语句 示例如下。 scala>for (i <-(0 until b.length).reverse) | printf("%d ",i) 5 4 3 2 1 0 不使用数组下标,直接遍历数组元素的语法格式如下。 for(e <-b) 循环体语句 示例如下。 scala>for(e <-b) | printf("%d ",e) 1 2 3 4 5 6 注意:变量e将先后被设为b(0)、b(1),以此类推。 【例3-2】 根据给定的整数数组创建一个新的数组,新数组中包括原数组中的全部值 为正数的元素,并以原有顺序排列,之后的元素则是数组中的零或负值,这些元素也以原有 顺序排列,如下所示。 scala>:paste // Entering paste mode (ctrl-D to finish) import scala.collection.mutable.ArrayBuffer //导入ArrayBuffer 包 val arr =Array(1,3,-3,-5,-7,3,2) val a =ArrayBuffer[Int]() val b =ArrayBuffer[Int]() arr.foreach(arg =>if(arg >0) a +=arg else b +=arg) a ++=b a.toArray // Exiting paste mode, now interpreting 按Ctrl+D组合键退出paste模式并执行代码,结果将如下所示。 a: scala.collection.mutable.ArrayBuffer[Int]=ArrayBuffer(1, 3, 3, 2, -3, -5, -7) b: scala.collection.mutable.ArrayBuffer[Int]=ArrayBuffer(-3, -5, -7) .. 3.6 for推导式 for推导式用来创建一个集合,其语法格式如下。 for () yield {} 30 Spark大数据处理技术与实战(Scala版·微课版) 从for推导式的语法格式可以看出,for循环的循环体将以yield开始,每次迭代生成集 合中的一个值,示例如下。 scala>val data =for( i <-1 to 5 ) yield {i} data: IndexedSeq[Int]=Vector(1, 2, 3, 4, 5) scala>print(data) Vector(1, 2, 3, 4, 5) scala>for(c<-"abcde";i<-0 to 1) yield c+i res34: IndexedSeq[Int]=Vector(97, 98, 98, 99, 99, 100, 100, 101, 101, 102) scala>for(c<-"abcde";i<-1 to 2) yield (c+i).toChar res32: IndexedSeq[Char]=Vector(b, c, c, d, d, e, e, f, f, g) scala>val data1=for(i<-1 to 5 if i%2 !=0;j <-10 until 11;sum=i+j) yield sum data1: IndexedSeq[Int]=Vector(11, 13, 15) scala>val names =Array("小丽", "唐诗涛", "刘涛","杨丽涛", "杨雪涛","杨鹏") scala>val filteredNames=for(x <-names;if x.contains("涛") && !x.startsWith ("杨")) yield x scala>filteredNames.foreach(println) 唐诗涛 刘涛 上面的“valfilteredNames=for(x < - names;ifx.contains("涛")&& ! x. startsWith("杨"))yieldx”的表达式还可以写成下面的式子。 val filteredNames =for {x <-names if x.contains("涛") && !x.startsWith("杨") } yield x 即将圆括号中的内容写到花括号中,注意这时要用换行的方式隔开它们,而不是使用分 号(;)。 .. 3.7 块表达式的赋值 在Java中,用花括号({})括起来的语句序列被称为块语句。当需要在选择分支或循环 中放置多个语句时,就需要使用块语句。 在Scala中,块语句包含一系列表达式,其结果也是一个表达式。块中最后一个表达式 的值就是块的值。这个特性对于那些需要分多步完成的初始化常量的操作很有用。如下 所示。 scala>val x0,y0=1 x0: Int =1 y0: Int =1 scala>val x=4;val y=5; x: Int =4 y: Int =5 scala>import scala.math._ //Scala 中,字符_ 是通配符,类似Java 中的* scala>val distance ={val dx =x -x0; val dy =y -y0; sqrt(dx * dx +dy * dy);} distance: Double =5.0 以上代码中块语句的值由最后一个表达式“sqrt(dx* dx+ dy* dy)”的值决定,变 量dx和dy仅为计算所需要的中间值。 第3章 Scala控制结构 31 以scala开头的包,在引入或使用时scala可以被省略。 scala>import math._ scala>sqrt(2) res0: Double =1.4142135623730951 .. 3.8 循环中的break和continue语句 在Scala中,类似Java的break、continue关键字被移除了。如果一定要实现Java中 break和continue关键字的功能,就需要使用scala.util.control包中Break类的breakable 静态类和break()方法,具体使用方法如下。 (1)导入Breaks包importscala.util.control.Breaks._。 (2)使用breakable静态类将for表达式包起来。 (3)在for表达式中需要退出循环的地方添加break()方法。 【例3-3】 使用for循环打印1~100的数字,如果数字到达50则退出for循环。 scala>:paste // Entering paste mode (ctrl-D to finish) import scala.util.control.Breaks._ breakable{ for(i <-1 to 100) { if(i >=50) break() else print(i+",") } } // Exiting paste mode, now interpreting 按Ctrl+D组合键退出paste模式并执行代码,结果如下所示。 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, 30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49, continue功能的实现与break类似,但有一点不同:实现break功能是用breakable静 态类将整个for表达式包起来,而实现continue功能只需要用breakable静态类将for表达 式的循环体包起来就可以了,具体示例如下。 【例3-4】 打印1~100的数字,使用for循环遍历这些数字,如果数字能整除2,则将之 跳过。 scala>:paste // Entering paste mode (ctrl-D to finish) import scala.util.control.Breaks._ for(i <-1 to 100 ) { breakable{ if(i %2 ==0) break() else print(i+",") } } 32 Spark大数据处理技术与实战(Scala版·微课版) // Exiting paste mode, now interpreting 按Ctrl+D组合键退出paste模式并执行代码,结果如下所示。 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53, 55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99, .. 3.9 习 题 1.有一个包含10个元素的数组,第一个元素的值是3,后面每个元素的值都是前面一 个元素的两倍加1,打印这个数组,然后将数组中奇数元素和偶数元素位置互换。 2.输入一个整数,将这个整数的所有约数放入一个数组,打印此数组。 3.请写出一个for循环,用i表示循环的变量,通过Range生成0~20的数字,并循环 打印出0~20这些数字中的奇数。 4.请写出一个for循环,用i表示循环的变量,通过Range生成0~20的数字,并循环 打印出0~20这些数字中的偶数(包括20)。 5.请使用嵌套for循环打印九九乘法表。