美文网首页
scala(四) For循环控制

scala(四) For循环控制

作者: 万事万物 | 来源:发表于2021-06-21 08:24 被阅读0次

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

    范围遍历

    to
    语法:

    for(i <-  0 to 10){语句块}
    

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

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

    until
    语法:

    for(i <-  0 until 10){语句块}
    

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

    for (i <- 0 until 10){
          println(s"i=$i")
        }
    
    i=0
    i=1
    i=2
    i=3
    i=4
    i=5
    i=6
    i=7
    i=8
    i=9
    

    to 与 until 底层实现

    to

    def to(end: Int): Range.Inclusive = Range.inclusive(self, end)
    

    until

    def until(end: Int): Range = Range(self, end)
    

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

    /**
     * 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 我们可以这么玩

    for (i <-  Range.inclusive(0,10,1)){
          println(s"i=$i")
    }
    
    i=0
    i=1
    i=2
    i=3
    i=4
    i=5
    i=6
    i=7
    i=8
    i=9
    i=10
    

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

    for (i <-  Range.inclusive(0,10,2)){
          println(s"i=$i")
    }
    
    i=0
    i=2
    i=4
    i=6
    i=8
    i=10
    

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

      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 我们可以这么玩

    for (i <- Range(0,10,1)){
          println(s"i=$i")
    }
    
    i=0
    i=1
    i=2
    i=3
    i=4
    i=5
    i=6
    i=7
    i=8
    i=9
    

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

    for (i <- Range(0,10,2)){
          println(s"i=$i")
    }
    
    i=0
    i=2
    i=4
    i=6
    i=8
    

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

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

    循环守卫

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

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

    for (i <-  0 to 10){
          if(i%2==0) {
            println(s"i=$i")
          }
    }
    
    i=0
    i=2
    i=4
    i=6
    i=8
    i=10
    

    使用循环守卫的方式:

        for (i <-  0 to 10 if i%2==0 ){
            println(s"i=$i")
        }
    
    i=0
    i=2
    i=4
    i=6
    i=8
    i=10
    

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

    for (i <-  0 to 10 if i%2==0) println(s"i=$i")
    

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

        for(i <- 0 to 10 ){
          print("输出一句话")
          if (i%2==0){
            print(s"i=$i")
          }
        }
    

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

        for(i <- 0 to 10 if i%2==0 if i%3==0 ){
              println(s"i=$i")
        }
    
    i=0
    i=6
    

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

        for(i <- 0 to 10   ){
          if (i%2==0){
            if (i%3==0){
              println(s"i=$i")
            }
          }
        }
    
    i=0
    i=6
    

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

        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 就行了

    for (i <-  0 to 10 by 3 ) println(s"i=$i")
    
    i=0
    i=3
    i=6
    i=9
    

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

    for (i <-  0 to 10 by 3 if i %2==0) println(s"i=$i")
    
    i=0
    i=6
    

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

     for (i <-  0 to 10 if i %2==0 by 3 ) println(s"i=$i")
    
    d:\project\scala\demo\src\main\scala\Demo01.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 的方式

        for (i <- 1 to 9){
          for (j <- 1 to i){
            print(s"$j * $i = ${i*j}\t")
          }
          println()
        }
    
    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 (i <- 1 to 9;j <- 1 to i){
          print(s"$j * $i = ${i*j}\t")
          if(j==i) println()
        }
    
    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 中写多个 循环条件,每个条件之间用;分隔。
    每个循环也是可以使用 循环守卫步长 的。

     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",所以对于这句代码,可以定义一个变量来接收它。

    比如,这样

    for (i <- 1 to 9 ;j <- 1 to i){
          val r= s"$j * $i = ${i*j}\t"
          print(r)
          if(j==i) println()
        }
    

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

      for (i <- 1 to 9 ;j <- 1 to i;r=s"$j * $i = ${i*j}\t"){
          print(r)
          if(j==i) println()
        }
    

    效果完全一样。

    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,就是一个()

     val r=for (i <- 1 to 9 ){
          i 
    }
    
    println(r)
    
    ()
    

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

        val r=for (i <- 1 to 9 ) yield{
          i 
        }
    
        println(r)
    
    Vector(1, 2, 3, 4, 5, 6, 7, 8, 9)
    

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

        val r=for (i <- 1 to 9 ) yield{
          i 
        }
    
        for(a <- r){
          println(a)
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    

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


    While循环控制

    基本语法

    循环变量初始化
    while (循环条件) {
          循环体(语句)
          循环变量迭代
    }
    

    说明

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

    import scala.util.control.Breaks._
    

    实现break功能

    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")
          }
        }
      }
    }
    
    i=0
    i=1
    i=2
    i=3
    i=4
    

    实现continue功能

    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")
          }
        }
      }
    }
    
    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

    相关文章

      网友评论

          本文标题:scala(四) For循环控制

          本文链接:https://www.haomeiwen.com/subject/otylyltx.html