循环结构

Scala同样提供了对程序流程控制的语法。Scala中的程序流程控制结构虽然与Java类似,但也有自己的一些独特的方法。

循环结构

经常地,我们需要遍历集合中的元素,或者对集合中的每个元素进行操作,或者从现有集合创建一个新集合,这就需要用到循环结构。Scala拥有与Java和C++相同的while和do-while循环。

下面是使用while的循环语句:

var sum = 0
var i = 1
while(i < 11) {
    sum += i
    i += 1
}
sum
i

输出结果如下:

sum: Int = 0 
i: Int = 1 

res26: Int = 55 
res27: Int = 11

请看下面的示例代码:

// while语句 
var i = 1
while (i <= 5) {
    println("hello")
    i = i + 1
}

//输出1到10的偶数
//方法1
var j = 1
while (j <= 10) {
  //如果j是偶数 
  if (j % 2 == 0) {
    println(j)
  }
  j = j + 1
}

//方法2
var k = 2
while (k <= 10) {
  println(k)
  k = k + 2
}

输出结果如下:

i: Int = 1 
hello 
hello 
hello 
hello 
hello 

j: Int = 1 
2 
4 
6 
8 
10 

k: Int = 2 
2 
4 
6 
8 
10

Scala 也有do-while循环,它和while循环类似,只是检查条件是否满足在循环体执行之后检查。例如:

var sum = 0
var i = 1
do {
    sum += i
    i += 1
} while(i <= 10)

println("1+...+10=" + sum)

Scala也提供了for循环和foreach方法。下面的代码使用for-each循环来遍历数组:

val a = Array("apple", "banana", "orange")
for (e <- a) println(e)

for (e <- a) {
   val s = e.toUpperCase
   println(s)
}

可以在for表达式内部定义变量,然后,可以在for表达式的body中重用这些变量,这称为"变量绑定"。如下代码所示:

val books = Array("Scala从入门到精通", "Groovy从入门到精通", 
                "Java从入门到精通", "24小时精通Scala", "24小时精通Java")

for {book <- books
    bookVa1 = book.toUpperCase()
    bookVa2 = book.toLowerCase()
} println(bookVa1 + "," + bookVa2)

注意to和until的区别。until不包含上边界值,而to包含上边界值。请看下面的代码:

object Hello {
  def main(args:Array[String]){   
    for(i <- 1 until 10){
      print(i + " ")      
    }
    
    println()
    
    for(i <- 1 to 10){
      print(i + " ")
    }
  }
}

输出结果如下所示:

1 2 3 4 5 6 7 8 9 
1 2 3 4 5 6 7 8 9 10 

【示例】遍历数组。

// 声明数组
var myList = Array(1.9, 2.9, 3.4, 3.5)
      
// 输出所有数组元素
for ( x <- myList ) {
    println( x )
}

// 计算数组所有元素的总和
var total = 0.0;
for ( i <- 0 to (myList.length - 1)) {
    total += myList(i);
}
println("总和为 " + total);

// 查找数组中的最大元素
var max = myList(0);
for ( i <- 1 to (myList.length - 1) ) {
    if (myList(i) > max) max = myList(i);
}
println("最大值为 " + max);

注:实际上可以用高阶函数,更加简洁地遍历数组(集合)。例如:

// 遍历 
var myList = Array(1.9, 2.9, 3.4, 3.5)
myList.map(println)
myList.foreach(println)

【示例】登录用户名密码的游戏:三次机会,从控制台输入输入用户名密码,如果成功登录,返回登录成功,失败,则反馈错误信息!如下:

import scala.io.StdIn

object Example02 {
  def main(args: Array[String]): Unit = {
    val dbUser = "mm"
    val dbPassword = "1234"
    var count = 3

    while(count > 0) {
      val username = StdIn.readLine("请输入账号:")
      val userpwd = StdIn.readLine("请输入密码:")
      if(username == dbUser && userpwd == dbPassword) {
        println(s"登陆成功,欢迎$username!")
        count = 0
      } else {
        count -= 1
        println("用户名或密码错误!您还有<$count>次机会!")
      }
    }
  }
}

在循环中使用if条件过滤值

在Scala中,还可以使用if子句来在遍历条目时添加过滤支持。例如,在下面的示例中,将水果名称长度超过2的过滤掉。

object demo {
  def main(args: Array[String]): Unit = {
    println("在for循环中使用if条件过滤值")
    val fruits = List("苹果","香蕉","桃子","猕猴桃","凤梨")
    for(fruit <- fruits if fruit.length<3){
      println(fruit)
    }
  }
}

输出结果如下所示:

在for循环中使用if条件过滤值
苹果
香蕉
桃子
凤梨

在for循环中使用if条件过滤值,并使用yield关键字返回结果。

object demo04 {
  def main(args: Array[String]): Unit = {
    println("\n在for循环中使用if条件过滤值,并使用yield关键字返回结果")
    val fruits = List("苹果","香蕉","桃子","猕猴桃","凤梨","杏","无籽西瓜","水晶葡萄")
    val fruitTwo = for {
      fruit <- fruits
      if fruit.length == 3 || fruit.length == 4
    } yield fruit
    println(s"过滤后的水果有 = $fruitTwo")
  }
}

输出结果如下所示:

在for循环中使用if条件过滤值,并使用yield关键字返回结果
过滤后的水果有 = List(猕猴桃, 无籽西瓜, 水晶葡萄)

在for循环中可以使用分号 (;) 来设置多个区间,它将迭代给定区间所有的可能值。以下示例代码演示了两个区间的循环实例:

for( a <- 1 to 3; b <- 1 to 3){
    print( "a = " + a )
    println( ", b = " + b )
}

输出结果如下所示:

a = 1, b = 1 
a = 1, b = 2 
a = 1, b = 3 
a = 2, b = 1 
a = 2, b = 2 
a = 2, b = 3 
a = 3, b = 1 
a = 3, b = 2 
a = 3, b = 3

【示例】遍历二维数组。

// 处理多维数组
var myMatrix = Array.ofDim[Int](3,3)
      
// 创建矩阵
for (i <- 0 to 2) {
    for ( j <- 0 to 2) {
        myMatrix(i)(j) = j;
    }
}
      
// 打印二维阵列
for (i <- 0 to 2; j <- 0 to 2) {
    print(" " + myMatrix(i)(j));
    if(j==2)  println()
}

println("-------------------")

// 多维数组 
val array = Array.ofDim[Int](2, 2)
array(0)(0) = 0
array(0)(1) = 1
array(1)(0) = 2
array(1)(1) = 3

for {
   i <- 0 to 1
   j <- 0 to 1
} println(s"($i)($j) = ${array(i)(j)}")

执行结果如下:

 0 1 2
 0 1 2
 0 1 2
------------------
(0)(0) = 0
(0)(1) = 1
(1)(0) = 2
(1)(1) = 3

【示例】编程输出九九乘法表。

// 九九乘法表 1
for(i <- 1 to 9) {
    for(j <- 1 to i){
        var ret = i * j
        print(s"$i*$j=$ret\t")
    }
    println()
}

// 九九乘法表 2
for(i <- 1 to 9; j <- 1 to i) {
    var ret = i * j
    print(s"$i*$j=$ret\t")
    if(i == j) {
        println
    }
}

输出结果如下:

1*1=1	
2*1=2	2*2=4	
3*1=3	3*2=6	3*3=9	
4*1=4	4*2=8	4*3=12	4*4=16	
5*1=5	5*2=10	5*3=15	5*4=20	5*5=25	
6*1=6	6*2=12	6*3=18	6*4=24	6*5=30	6*6=36	
7*1=7	7*2=14	7*3=21	7*4=28	7*5=35	7*6=42	7*7=49	
8*1=8	8*2=16	8*3=24	8*4=32	8*5=40	8*6=48	8*7=56	8*8=64	
9*1=9	9*2=18	9*3=27	9*4=36	9*5=45	9*6=54	9*7=63	9*8=72	9*9=81	
--------------
1*1=1	
2*1=2	2*2=4	
3*1=3	3*2=6	3*3=9	
4*1=4	4*2=8	4*3=12	4*4=16	
5*1=5	5*2=10	5*3=15	5*4=20	5*5=25	
6*1=6	6*2=12	6*3=18	6*4=24	6*5=30	6*6=36	
7*1=7	7*2=14	7*3=21	7*4=28	7*5=35	7*6=42	7*7=49	
8*1=8	8*2=16	8*3=24	8*4=32	8*5=40	8*6=48	8*7=56	8*8=64	
9*1=9	9*2=18	9*3=27	9*4=36	9*5=45	9*6=54	9*7=63	9*8=72	9*9=81	

中断循环

Scala并没有提供break或continue语句来退出循环。那么如果需要break时我们该怎么做呢?有如下几个选项:

  • 1) 使用Boolean型的控制变量。
  • 2) 使用嵌套函数——可以从函数当中return。
  • 3) 使用Breaks对象中的break方法。

break方法使用如下面的示例代码所示:

import scala.util.control.Breaks._

var n = 15
breakable {
    for(c <- "Spark Scala Storm") {
        if(n == 10) {
            println()
            break
        } else {
            print(c)
        }
        n -= 1
    }
}

输出内容如下:

import scala.util.control.Breaks._ 
n: Int = 15 
Spark

循环中使用计数器

使用for-each循环有一个缺点,就是无法获取当前索引值。为此,可使用循环计数器。请看以下的代码:

    val a = Array("张三","李四","王老五")

    // 循环计数
    for (i <- 0 until a.length) {
       println(s"$i:${a(i)}")
    }

输出结果如下:

0:张三 
1:李四 
2:王老五

Scala集合还提供了一个zipWithIndex方法可用于创建循环计数器:

    for ((e, index) <- a.zipWithIndex) {
        println(s"$index: $e")
    }

输出结果如下:

0:张三 
1:李四 
2:王老五

循环中使用条件过滤

可以遍历的同时执行条件过滤:

// for语句
for (e <- 1 to 5) {
  println(e)
}

// 只输出偶数
for (e <- 1 to 10  if  e % 2 == 0) {    
    println(e)      
}

// 迭代
val books = Array("Scala从入门到精通", "Groovy从入门到精通", 
                "Java从入门到精通", "24小时精通Scala", "24小时精通Java")
                  
for(book <- books){
    print(book + " || ")
}
println("\n---------------------------")

// 过滤
for(book<-books if book.contains("Scala")){ 
    println(book) 
}

Scala可以使用一个或多个 if 语句来过滤一些元素:

val numList = Array(1,2,3,4,5,6,7,8,9,10);

// for 循环
for( a <- numList
    if a != 3; if a < 8 ){
      println( "a = " + a );
}

输出结果如下:

a = 1 
a = 2 
a = 4 
a = 5 
a = 6 
a = 7

可以为嵌套循环通过if表达式添加条件:

for (i <- 1 to 3; j <- 1 to 3 if i != j) 
    print ((10 * i + j) + " ")

println

// if表达式是否添加括号,结果无变化:
for (i <- 1 to 3; j <- 1 to 3  if (i != j)) 
    print ((10 * i + j) + " ")

输出结果如下:

12 13 21 23 31 32 
12 13 21 23 31 32 

【示例】在for条件表达式中使用过滤。

    // 打印所有的偶数
    for (i <- 1 to 10 if i % 2 == 0) println(i)

    // 也可写成下面这样
    for {
      i <- 1 to 10
      if i % 2 == 0
    } println(i)

    // 多条件
    for {
      i <- 1 to 10
      if i > 3
      if i < 6
      if i % 2 == 0
    } println(i)

《PySpark原理深入与编程实战》