Scala专题系列(二):Scala控制结构

2022-04-18 13:53:36 浏览数 (1)

本片主要内容主要内容包含Scala条件表达式,循环和函数,同时你将学到Scala编程与其它变成语言之间的一种差异。在Java和C 中,表达式和语句是截然不同的,表达式有值而语句只是执行动作。而在Scala中,几乎所有构造出来的语法结构都有值,从而使得程序更加精简,易读

1 :条件表达式

Scala中if/else 语法结构和java,C 一样,不过,在Scala中if/else表达式有值,这个值就是跟在if或者else之后的表达式的值,例如:

if(x >0 ) 1 else -1

那么我们可以这样来写

val s = if(x >0 ) 1 else -1

这个表达式是有值得,最终的值就是1或者-1 ,以上的写法在java中就是

if(x > 0) s = 1 else s = -1

明显的第一种写法会更好一些,因为它可以用来初始化一个val,而在第二种写法中,变量必须是var的

在Java中,有一个三元表达式比如:int i = n > 0 ? 1 : 0

那么这个在scala中就是val s = if(x >0 ) 1 else -1

在Scala中,每个表达式都有一个类型,比如上面你的表达式返回类型是int类型,因为两个分支都是int类型的,但是如果两个分支的类型不同,那么最终返回的就是Scala中的超类Any,Any在Scala中是所有类型的超类

val res = if(n > 0) "result" else 1

比如上面的一条语句,返回的类型就是Any的

2 : 循环

在scala中,while循环和在java与C 中一样

代码语言:javascript复制
while(n > 0){
    n -= 1
}

在scala中没有与for循环直接对应的结构,如果需要这样的循环我们可以使用while或者使用scala版的for

代码语言:javascript复制
for( i <- 1 to n ){
    println(i)
}

1 to n 指的是循环1到n个元素索引。

表达式结构:for(i <- 表达式)

让变量i遍历<- 右边的表达式的所有值

在遍历字符串或者数组时,通常需要使用0 到 n-1的区间,这个时候可以用 util方法而不是to,util方法会犯一个并不包含上限的区间

代码语言:javascript复制
val s = "hello"
var sum = 0
for(i <- i util s.length) sum  = s(i)

或者我们并不需要下标

代码语言:javascript复制
var sum = 0 
for( i <- "hello" ) sum  = i 

Scala中并没有提供break或者continue语句来退出循环,那么囚需要break时,该如何做呢

1.使用Boolean类型来控制变量

2:使用嵌套函数-在函数当中return

3:使用Breaks对象中的break方法

代码语言:javascript复制
breakable{ 
    for( ... ){
    if(...) break 
}
 }

3 : 高级for循环和for推导式

在上面循环中我们只看到了基本的for循环,在scala中for循环比起java和c 功能要丰富很多。下面来看一看for循环的高级特性

可以以变量<-表达式的形式提供多个生成器,用分号将它们隔开

代码语言:javascript复制
for( i <- 1 to 3 ; j <- 1 to 3 ) println((i   j)   " ")

每个生成器都带一个守卫,以if开头的Boolean表达式

代码语言:javascript复制
for( i <- 1 to 3; j<-1 to 3  if i != j ) println((i   j)   " ") 

注意:在if之前并没有分号

也可以使用任意多的定义,引入可以在循环中使用的变量

代码语言:javascript复制
for( i <- 1 to 3 ; from = 4 - i ; j <- from to 3 ) println(i j)

如果for循环的循环体以yield开始,则该循环会构造出一个集合,每次迭代生成集合中的一个值,比如:

代码语言:javascript复制
for(i <- 1 to 10) yield i % 3 
  // 生成 Vector(1,2,0,1,2,0,1,2,0,1)

这类循环叫做for推导式

假如你并不需要打印过滤后的集合,你需要编写代码对过滤后的集合进行处理,那么该怎

么办呢?使用 yield 关键字便能在 for 表达式中生成新的集合。比如:

代码语言:javascript复制
val dogBreeds = List("Doberman", "Yorkshire Terrier", "Dachshund",
"Scottish Terrier", "Great Dane", "Portuguese Water Dog")
val filteredBreeds = for {
breed <- dogBreeds
if breed.contains("Terrier") && !breed.startsWith("Yorkshire")
} yield breed

每次执行 for 表达式时,过滤后的结果将生成 breed 值。随着代码的执行,这些结果

值逐渐积累起来,累计而成的结果值集合被赋给了 filteredBreeds 对象。 for-yield

表达式所生成的集合类型将根据被遍历的集合类型推导而出。在上面的例子中,由

于 filteredBreeds 源 于 dogBreeds 列 表, 而 dogBreeds 类 型 为 List[String], 因 此

filteredBreeds 的类型为 List[String]。

for 推导式有一个不成文的约定:当 for 推导式仅包含单一表达式时使用原

括号,当其包含多个表达式时使用大括号。值得注意的是,使用原括号时,

早前版本的 Scala 要求表达式之间必须使用分号。

4 : 懒值(lazy)

当val被声明为lazy时,那么该变量的初始化将被推迟,直到我们首次对它取值。例如

lazy val words = scala.io.Source.fromFile("/usr/local/dic/words").mkString

上面我们定义了一个lazy 的words,如果我们在后面的程序中一直没有调用words,那么这个word就一直不会执行,一直到我们去调用它才会去执行文件的读取操作。

由于表达式执行代价昂贵(例如: 打开一个数据库连接), 因此我们希望能推迟该操作,

直到我们确实需要表达式结果值时才执行它。

• 为了缩短模块的启动时间,可以将当前不需要的某些工作推迟执行。

• 为了确保对象中其他的字段的初始化过程能优先执行,需要将某些字段惰性化

那么惰性赋值与方法调用有那些差别呢?对于方法调用而言,每次调用方法时方法体都会

被执行;而惰性赋值则不然,首次使用该值时,用于初始化的“代码体”才会被执行一

次。这种只能执行一次的计算对于可变字段而言几乎没有任何意义。因此, lazy 关键字并

不能用于修饰 var 变量。

0 人点赞