scala(四) For循环控制

2022-04-12 15:50:28 浏览数 (1)

Scala也为for循环这一常见的控制结构提供了非常多的特性,这些for循环的特性被称为for推导式或for表达式。 范围数据循环

范围遍

to 语法:

代码语言:javascript复制
for(i <-  0 to 10){语句块}

to :表示包含结尾,会将10也输出来

代码语言:javascript复制
  def main(args: Array[String]): Unit = {
    for (i <- 0 to 10){
      println(s"i=$i")
    }
  }
代码语言:javascript复制
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10

until 语法:

代码语言:javascript复制
for(i <-  0 until 10){语句块}

until :不包含结尾,所以不会将10也输出来

代码语言:javascript复制
for (i <- 0 until 10){
      println(s"i=$i")
    }
代码语言:javascript复制
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9

to 与 until 底层实现

to

代码语言:javascript复制
def to(end: Int): Range.Inclusive = Range.inclusive(self, end)

until

代码语言:javascript复制
def until(end: Int): Range = Range(self, end)

他们都依赖于一个伴生对象 Range。 to 的实现

代码语言:javascript复制
/**
 * start 开始坐标 如:0
 * end 结束坐标 如:10
 * step 步长,后面会说
 */
final class Inclusive(start: Int, end: Int, step: Int) extends Range(start, end, step) {
//    override def par = new ParRange(this)
    override def isInclusive = true
    override protected def copy(start: Int, end: Int, step: Int): Range = new Inclusive(start, end, step)
  }

于是关于 to 我们可以这么玩

代码语言:javascript复制
for (i <-  Range.inclusive(0,10,1)){
      println(s"i=$i")
}
代码语言:javascript复制
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10

上面的案例将步长设置为1,表示一个一个执行,也可以跨步执行;如跨两步

代码语言:javascript复制
for (i <-  Range.inclusive(0,10,2)){
      println(s"i=$i")
}
代码语言:javascript复制
i=0
i=2
i=4
i=6
i=8
i=10

了解完to,回头看看until apply:类似于构造器,until 并不是采用 Range 内部方法实现,而是采用 Range 的构造器实现。

代码语言:javascript复制
  def apply(start: Int, end: Int, step: Int): Range = new Range(start, end, step)
  def apply(start: Int, end: Int): Range = new Range(start, end, 1)

所以关于 until 我们可以这么玩

代码语言:javascript复制
for (i <- Range(0,10,1)){
      println(s"i=$i")
}
代码语言:javascript复制
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9

第三个参数和 to 一样,用于定义步长,依然写个案例跨两步执行。

代码语言:javascript复制
for (i <- Range(0,10,2)){
      println(s"i=$i")
}
代码语言:javascript复制
i=0
i=2
i=4
i=6
i=8

总结:关于 scala 中的范围查找有两种,

  1. to:包含边界
  2. until:不包含边界
  3. 底层实现依赖于一个伴生对象 Range

循环守卫

循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过,类似于continue。

案例:打印0-10 中为偶数的数字

代码语言:javascript复制
for (i <-  0 to 10){
      if(i%2==0) {
        println(s"i=$i")
      }
}
代码语言:javascript复制
i=0
i=2
i=4
i=6
i=8
i=10

使用循环守卫的方式:

代码语言:javascript复制
    for (i <-  0 to 10 if i%2==0 ){
        println(s"i=$i")
    }
代码语言:javascript复制
i=0
i=2
i=4
i=6
i=8
i=10

在scala 中由于代码块只有一行代码,那么可以简写成这样。

代码语言:javascript复制
for (i <-  0 to 10 if i%2==0) println(s"i=$i")

关于循环守卫 只能内容完全在if 判断中,才能使用 循环守卫 错误示例:这种方式就无法使用循环守卫,因为有内容处于判断外。

代码语言:javascript复制
    for(i <- 0 to 10 ){
      print("输出一句话")
      if (i%2==0){
        print(s"i=$i")
      }
    }

多个循环守卫,这种方式是可以的。语法就是 后面定义多个 if 即可。

代码语言:javascript复制
    for(i <- 0 to 10 if i%2==0 if i%3==0 ){
          println(s"i=$i")
    }
代码语言:javascript复制
i=0
i=6

多个if 的关系,是 && 的关系;必须所有的if满足才可以,如同这样;

代码语言:javascript复制
    for(i <- 0 to 10   ){
      if (i%2==0){
        if (i%3==0){
          println(s"i=$i")
        }
      }
    }
代码语言:javascript复制
i=0
i=6

既然多个循环守卫&& 的关系,那有没有 || 的关系呢? 如:

代码语言:javascript复制
    for(i <- 0 to 10){
      if (i%2==0){
        println(s"i=$i")
      }
      if(i%3==0 ){
        println(s"i=$i")
      }
      
    }

答案:没有,至少我不知道,循环守卫如何实现,知道的朋友请告诉我。

循环步长

前面也有提过步长,当对于使用 Range 太麻烦了,也不推荐使用,使用 by 可以用于定义步长。底层实现还是基于Range

语法什么的也不知道怎么描述,直接演示用法吧,记住在 范围查找 to 或 until 后 及 by 就行了

代码语言:javascript复制
for (i <-  0 to 10 by 3 ) println(s"i=$i")
代码语言:javascript复制
i=0
i=3
i=6
i=9

当然也可以和循环守卫组合使用

代码语言:javascript复制
for (i <-  0 to 10 by 3 if i %2==0) println(s"i=$i")
代码语言:javascript复制
i=0
i=6

注意:千万不要将循环守卫写到 by 前面。 错误示范:

代码语言:javascript复制
 for (i <-  0 to 10 if i %2==0 by 3 ) println(s"i=$i")
代码语言:javascript复制
d:projectscalademosrcmainscalaDemo01.scala:6: error: value by is not a member of Boolean
    for (i <-  0 to 10 if i %2==0 by 3 ) println(s"i=$i")
                                  ^
one error found

至于为什么

  1. 语法不支持。
  2. 这种方式存在歧义,无法理解。

嵌套循环

所谓嵌套循环就是循环内在写一个循环。 案例:打印九九乘法表 方式一:java 的方式

代码语言:javascript复制
    for (i <- 1 to 9){
      for (j <- 1 to i){
        print(s"$j * $i = ${i*j}t")
      }
      println()
    }
代码语言:javascript复制
1 * 1 = 1   
1 * 2 = 2   2 * 2 = 4   
1 * 3 = 3   2 * 3 = 6   3 * 3 = 9   
1 * 4 = 4   2 * 4 = 8   3 * 4 = 12  4 * 4 = 16  
1 * 5 = 5   2 * 5 = 10  3 * 5 = 15  4 * 5 = 20  5 * 5 = 25  
1 * 6 = 6   2 * 6 = 12  3 * 6 = 18  4 * 6 = 24  5 * 6 = 30  6 * 6 = 36  
1 * 7 = 7   2 * 7 = 14  3 * 7 = 21  4 * 7 = 28  5 * 7 = 35  6 * 7 = 42  7 * 7 = 49  
1 * 8 = 8   2 * 8 = 16  3 * 8 = 24  4 * 8 = 32  5 * 8 = 40  6 * 8 = 48  7 * 8 = 56  8 * 8 = 64  
1 * 9 = 9   2 * 9 = 18  3 * 9 = 27  4 * 9 = 36  5 * 9 = 45  6 * 9 = 54  7 * 9 = 63  8 * 9 = 72  9 * 9 = 81  

方式二:scala 的方式

代码语言:javascript复制
for (i <- 1 to 9;j <- 1 to i){
      print(s"$j * $i = ${i*j}t")
      if(j==i) println()
    }
代码语言:javascript复制
1 * 1 = 1   
1 * 2 = 2   2 * 2 = 4   
1 * 3 = 3   2 * 3 = 6   3 * 3 = 9   
1 * 4 = 4   2 * 4 = 8   3 * 4 = 12  4 * 4 = 16  
1 * 5 = 5   2 * 5 = 10  3 * 5 = 15  4 * 5 = 20  5 * 5 = 25  
1 * 6 = 6   2 * 6 = 12  3 * 6 = 18  4 * 6 = 24  5 * 6 = 30  6 * 6 = 36  
1 * 7 = 7   2 * 7 = 14  3 * 7 = 21  4 * 7 = 28  5 * 7 = 35  6 * 7 = 42  7 * 7 = 49  
1 * 8 = 8   2 * 8 = 16  3 * 8 = 24  4 * 8 = 32  5 * 8 = 40  6 * 8 = 48  7 * 8 = 56  8 * 8 = 64  
1 * 9 = 9   2 * 9 = 18  3 * 9 = 27  4 * 9 = 36  5 * 9 = 45  6 * 9 = 54  7 * 9 = 63  8 * 9 = 72  9 * 9 = 81  

向上面这种方式;scala 支持在 一个 for 中写多个 循环条件,每个条件之间用;分隔。 每个循环也是可以使用 循环守卫步长 的。

代码语言:javascript复制
 for (i <- 1 to 9 by 2 if i%2==0;j <- 1 to i by 2 if j%2==0){
      print(s"$j * $i = ${i*j}t")
      if(j==i) println()
    }

这样写是没有问题的,但是嵌套太多,可能会把代码变得很复杂,谨慎使用

引入变量

以 上面的 九九乘法表为例,最重要的代码就是 s"$j * $i = ${i*j}t",所以对于这句代码,可以定义一个变量来接收它。

比如,这样

代码语言:javascript复制
for (i <- 1 to 9 ;j <- 1 to i){
      val r= s"$j * $i = ${i*j}t"
      print(r)
      if(j==i) println()
    }

哈哈,开个玩笑,肯定不是这样的,下面才是正确的用法;

代码语言:javascript复制
  for (i <- 1 to 9 ;j <- 1 to i;r=s"$j * $i = ${i*j}t"){
      print(r)
      if(j==i) println()
    }

效果完全一样。

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

注意的是,循环中引入变量是不用定义 valvar的,这点要注意。

循环返回值

无论是if 判断还是 for 循环,他们都遵循一个规则,就是块表达式。只要是块表达式,那么就有表达式,即便是Unit

for 循环中的返回值,默认就是 Unit,就是一个()

代码语言:javascript复制
 val r=for (i <- 1 to 9 ){
      i 
}

println(r)
代码语言:javascript复制
()

很显然并不是我们想要的结果,这里需要使用到一个关键字 yeild

代码语言:javascript复制
    val r=for (i <- 1 to 9 ) yield{
      i 
    }

    println(r)
代码语言:javascript复制
Vector(1, 2, 3, 4, 5, 6, 7, 8, 9)

Vector 属于一个迭代器,可以直接遍历它。

代码语言:javascript复制
    val r=for (i <- 1 to 9 ) yield{
      i 
    }

    for(a <- r){
      println(a)
    }
代码语言:javascript复制
1
2
3
4
5
6
7
8
9

项目需求中,很有场景需要我们从一个集合中找到符合条件的数据并返回。我们常用做法就是在循环外部定义一边数组或集合,把符合条件的结果写入到集合中,最后返回集合。对于这种场景,无论是python还是scala都有yeild实现。

While循环控制

基本语法

代码语言:javascript复制
循环变量初始化
while (循环条件) {
      循环体(语句)
      循环变量迭代
}

说明

  1. 循环条件是返回一个布尔值的表达式
  2. while循环是先判断再执行语句
  3. 与if语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()
  4. 因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在while循环的外部,那么就等同于循环的内部对外部的变量造成了影响,也就违背了函数式编程的重要思想(输入=>函数=>输出,不对外界造成影响),所以不推荐使用,而是推荐使用for循环。
代码语言:javascript复制
var i =0
    while (i<10){
      println(s"i=${i}")
      i =1
    }
代码语言:javascript复制
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9

循环中断

在 java 中有breakcontinue 关键字,实现循环中断

break:终止整个循环 continue:结束当次循环

在 scala 中并没有 breakcontinue 关键字。 若需要使用以上两种功能,使用使用到scala中的 Breaks 类 导入相关包

代码语言:javascript复制
import scala.util.control.Breaks._

实现break功能

代码语言:javascript复制
import scala.util.control.Breaks._

object Demo01  {

  def main(args: Array[String]): Unit = {
    
    breakable{
      for(i <- 0 to 10){
        if(i==5){
          break()
        }
        println(s"i=$i")
      }
    }
  }
}
代码语言:javascript复制
i=0
i=1
i=2
i=3
i=4

实现continue功能

代码语言:javascript复制
import scala.util.control.Breaks._

object Demo01  {

  def main(args: Array[String]): Unit = {
    for(i <- 0 to 10){
      breakable{
        if(i==5){
          break()
        }
        println(s"i=$i")
      }
    }
  }
}
代码语言:javascript复制
i=0
i=1
i=2
i=3
i=4
i=6
i=7
i=8
i=9
i=10

基本说明:scala 内置控制结构特地去掉了 break 和 continue,是为了更好的适应 函数式编程,推荐使用函数式编程的风格解决 break 和 continue 的功能,而不是一个关键字。scala中使用 breakable 控制结构来实现 break 和 continue 功能。

在 scala 中 已经用 循环守卫 更好的代替了 continue 关键字。

break()底层实现:

在 java 中除了使用 break 关键字进行循环中断外,还可以使用 异常的方式。在 scala 语言中,breakable 底层实现就是采用 异常的方式,代替了 break 。 def break(): Nothing = throw breakException

0 人点赞