美文网首页
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