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 的实现
/**
* 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 的构造器实现。
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 中的范围查找有两种,
- to:包含边界
- until:不包含边界
- 底层实现依赖于一个伴生对象 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 判断中,才能使用 循环守卫
错误示例:这种方式就无法使用循环守卫,因为有内容处于判断外。
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满足才可以,如同这样;
for(i <- 0 to 10 ){
if (i%2==0){
if (i%3==0){
println(s"i=$i")
}
}
}
代码语言:javascript复制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")
代码语言:javascript复制i=0
i=3
i=6
i=9
当然也可以和循环守卫
组合使用
for (i <- 0 to 10 by 3 if i %2==0) println(s"i=$i")
代码语言:javascript复制i=0
i=6
注意:千万不要将循环守卫
写到 by
前面。
错误示范:
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
至于为什么
- 语法不支持。
- 这种方式存在歧义,无法理解。
嵌套循环
所谓嵌套循环就是循环内在写一个循环。
案例:打印九九乘法表
方式一:java 的方式
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 中写多个 循环条件,每个条件之间用;
分隔。
每个循环也是可以使用 循环守卫
和 步长
的。
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
注意的是,循环中引入变量
是不用定义 val
或var
的,这点要注意。
循环返回值
无论是if 判断还是 for 循环,他们都遵循一个规则,就是块表达式
。只要是块表达式
,那么就有表达式,即便是Unit
。
for 循环中的返回值,默认就是 Unit
,就是一个()
val r=for (i <- 1 to 9 ){
i
}
println(r)
代码语言:javascript复制()
很显然并不是我们想要的结果,这里需要使用到一个关键字 yeild
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 (循环条件) {
循环体(语句)
循环变量迭代
}
说明:
- 循环条件是返回一个布尔值的表达式
- while循环是先判断再执行语句
- 与if语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()
- 因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在while循环的外部,那么就等同于循环的内部对外部的变量造成了影响,也就违背了函数式编程的重要思想(输入=>函数=>输出,不对外界造成影响),所以不推荐使用,而是推荐使用for循环。
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 中有break
和continue
关键字,实现循环中断
break:终止整个循环 continue:结束当次循环
在 scala 中并没有 break
和continue
关键字。
若需要使用以上两种功能,使用使用到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")
}
}
}
}
代码语言:javascript复制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")
}
}
}
}
代码语言: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