第3章 控制结构和函数
Java 中把表达式和语句当成两个不同的东西,表达式有值,语句没有值,
只是控制结构。在 Scala 中,几乎所有语法结构都有值。
3.1 If else 表达式
scala> val x = 5 x: Int = 5 scala> val s = if( x > 0 ) 1 else -1 s: Int = 1
S 为什么会有值?
If 语句的返回类型取决于最后一条语句。语句后面的分号不是必须的。
Scala 没有三元运算符,不需要。
If 表达式会有一个返回值类型,如果 if 或者 else 返回的类型不一样,就
返回 Any 类型(所有类型的公共超类型)。
scala> val s1 = if( x > 0) "hello" else -1 s1: Any = hello scala> val s3 = if ( x < 0 ) "hello" s3: Any = ()
如果缺少一个判断,什么都没有返回,但是 Scala 认为任何表达式都会
有值,对于空值,使用 Unit 类,写做()【叫做无有用占位符,相当于 java
中的 void】
scala> val s4 = if(x < 0 ) "hello" else () s4: Any = ()
注:行尾的位置不需要分号,只要能够从上下文判断出语句的终止即可。但是如果
在单行中写多个语句,则需要分号分割。在 Scala 中,{}快包含一系列表达
式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。
3.2 while 表达式
Scala 提供和 Java 一样的 while 和 do 循环,与 If 语句不同,While 语句
本身没有值,即整个 While 语句的结果是 Unit 类型的()。
while (n > 0) { r = r * n n -= 1 println(r) } do{ r = r * n n -= 1 println(r) }while(n > 0)
注:scala 并没有提供 break 和 continue 语句来退出循环,如果需要 break,可以
通过几种方法来做 1、使用 Boolean 型的控制变量 2、使用嵌套函数,从函数中
return 3、使用 Breaks 对象的 break 方法。
//循环的终止 import scala.util.control.Breaks val looper = new Breaks() //括号可以省略 var count = 0 looper.breakable{ // . 可以替换成空格 while(count <= 100){ count += 1 println(count) if(count == 10){ looper.break() } }
//多重while循环的终止 val whileLooper1 = new Breaks val whileLooper2 = new Breaks whileLooper1.breakable{ while (true){ count += 1 if(count == 10){ whileLooper1.break() } whileLooper2.breakable{ while (true){ if(count == 20){ whileLooper2.break() } } } } }
3.3 for 表达式
Scala 也为 for 循环这一常见的控制结构提供了非常多的特性,这些 for
循环的特性被称为 for 推导式(for comprehension)或 for 表达式(for
expression)。
推导式一词起源于函数式编程。
像 变量名 <- 集合 这样的表达式也被称为生成器表达式,该表达式会
基于集合生成单独的数值。
//多重for循环 for(i <- 1 to 3; j <-1 to 3){ println(i + "----" + j) println() }
//输出10 9 8 7 ... 1 for(i <- Range(10, 0, -1)){ println(i) } //输出1~9 for(i <- 1 until 10){ println(i) }
保护式,也叫守卫,可以添加一个或者多个守卫,不需要 continue 语句
//实现1~10的输出,只输出偶数 for (i <- 1 to 10 if i % 2 == 0){ println(i) }
For 推导式可以引入变量
//在for循环中添加变量表达式 for(i <- 1 to 10; j = 10 - i; m = j * i){ println("j:" + j) println("m:" + m) }
需要返回值怎么办?
使用 yield 关键字便能在 for 表达式中生成新的集合。for-yield 表达式所
生成的集合类型将根据被遍历的集合类型推导而出。
scala> val forResult = for (i <- 1 to 10) yield i forResult: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
尖叫提示:{}和()对于 for 表达式来说都可以。for 推导式有一个不成文的约定:当
for 推导式仅包含单一表达式时使用原括号,当其包含多个表达式时使用大括
号。值得注意的是,使用原括号时,早前版本的 Scala 要求表达式之间必须
使用分号。
//使用花括号作为for循环的条件包裹 for{ i <- 1 to 3 m = i * 2 } println(m) //需要使用具体的逻辑
Scala 随堂笔记
一、变量的声明 ** val:初始化后不能被再次赋值 ** var:初始化之后可以多次复制 ** if else:有返回值,如果多个逻辑控制代码块,返回不同的值,则返回为:Any ** 循环: ** while -- 返回值类型为Unit -> () -- break的用法 ** for -- 基本形式 -- 守卫 -- 表达式 -- 多个循环体放在同一个参数体内 -- 输出遍历元素到一个集合中并返回:yeild i -- {}花括号形式,用的不多
3.4 函数
Scala 提供了很多函数,如果 math 函数,Java 中通常通过静态方法来模
拟。函数定义如下:
def 函数名(参数名:类型…)[: 返回类型] = 函数体
比如:
def play(a1 : Int, a2 : Int) : String = {
(a1 + "," + a2) }
def play(a1 : Int, a2 : Int) = { //返回值可以省略
a1 + a2 }
def paly(a1 : Int, a2 : Int) = a1 + a2
def paly = 1;
//标准形式 def play1(a1: Int, a2: Int): String = { String.valueOf(a1 + a2) } println(play1(10, 20)) //省略返回值形式(返回的是循环中最后一行的值) def play2(a1: Int, a2: Int) = { a1 + a2 } println(play2(10, 10)) //省略花括号形式 def play3(a1: Int, a2: Int) = a1 + a2 println(play3(5, 5)) //返回Unit类型的函数 def play4(a1: String) : Unit = { println(a1) } //返回Unit类型的函数,但是没有显示指定返回类型。(当然也可以返回非Unit类型的值) def play5(a1: String) = { println(a1) } //返回类型有多种可能,此时也可省略Unit def play6(a1: String) = { if(a1.length >= 3) a1 + "hello" else 3 } //可选参数 def play7(a1: Int, a2: Int = 10) = { a1 + a2 } println(play7(1)) //变长参数 def play8(args: Int*) = { for(i <- args){ println(i) } } play8(1,2,3,4,5) //递归函数:在使用的时候必须要有明确的返回值类型 //递归实现阶乘 def factorial(n : Int) : Int = { if(n == 1) n else factorial(n - 1) * n } println(factorial(3))
注:1、Scala 可以通过=右边的表达式 推断出函数的返回类型。如果函数体需要
多个表达式,可以用代码块{}。
2、可以把 return 当做 函数版本的 break 语句。
3、递归函数一定要指定返回类型。
4、变长参数通过* 来指定,所有参数会转化为一个 seq 序列
5、_* 告诉编译器 Range 当做参数序列化处理。
6、Head 是获得首元素,tail 是获取剩下元素的序列。
3.5 过程
我们将不返回值得函数叫做过程,返回类型是 Unit,他是没有=号的。
1)定义过程示例1:(显示的返回值类型为Unit)
def shout1(content: String) : Unit = {
println(content)
}
2)定义过程示例2:(隐式的返回值类型是Unit)
def shout1(content: String) = {
println(content)
}
3)定义过程示例3:(没有 = 号)
def shout1(content: String) {
println(content)
}
3.6 匿名函数
//匿名函数 /* * 1、匿名函数右边的函数体应该使用 => 符号来指定 * 2、匿名函数的函数体,可以是一个包含多行代码的代码块 * 3、函数的类型,为参数个数的类型 */ val play10 = () => 0 println(play10) //<function0> 表示传参个数为0 println(play10()) //0 val play11 = (a1 : Int) => 0 println(play11) //<function1> 表示传参个数为1 val play12 = (a1 : Int, a2 : Int) => { a1 + a2 a2 - 10 + a1 } println(play12) //<function2> println(play12(10, 20)) //20
3.7 懒值
当 val 被声明为 lazy 时,他的初始化将被推迟,直到我们首次对此取
值。
懒值使用注意:
1) 用于初始化开销比较大的语句。
2) 可以解决循环依赖问题。
3) 是开发懒数据结构的基础。
1)lazy 示例:通过 lazy 关键字的使用与否,来观察执行过程
def play1(a1: Int) = { println("play1方法被执行," + "a1:" + a1) a1 } lazy val l1 = play1(10) val l2 = play1(20) println("l1变量定义完毕") println(l1) /* 结果: play1方法被执行,a1:20 lazy变量定义完毕 play1方法被执行,a1:10 10 */
3.8 异常
当碰到异常情况时,方法抛出一个异常,终止方法本身的执行,异常传
递到其调用者,调用者可以处理该异常,也可以升级到它的调用者。运行系
统会一直这样升级异常,直到有调用者能处理它。 如果一直没有处理,则终
止整个程序。
Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“受检”异常,你
不需要声明说函数或者方法可能会抛出某种异常。受检异常在编译器被检
查,java 必须声明方法所会抛出的异常类型。
抛出异常:用 throw 关键字,抛出一个异常对象。所有异常都是
Throwable 的子类型。throw 表达式是有类型的,就是 Nothing,因为 Nothing
是所有类型的子类型,所以 throw 表达式可以用在需要类型的地方。
//并不像 Java 代码那样,需要声明方法会抛出异常,这给程序员省去理论很多烦恼。 def divide(x: Int, y: Int): Int = { if (y == 0) throw new Exception("Divide by zero") else x / y }
捕捉异常:
在 Scala 里,借用了模式匹配的思想来做异常的匹配,因此,在 catch 的
代码里,是一系列 case 字句。
异常捕捉的机制与其他语言中一样,如果有异常发生,catch 字句是按次
序捕捉的。因此,在 catch 字句中,越具体的异常越要靠前,越普遍的异常越
靠后。 如果抛出的异常不在 catch 字句中,该异常则无法处理,会被升级到
调用者处。
异常捕获通常采用模式匹配的语法:
try { process(in) } catch { case ex: IOException => println(ex) } finally { in.close()}
finally 字句用于执行不管是正常处理还是有异常发生时都需要执行的步
骤,一般用于对象的清理工作。
注:1、Throw 的类型为 Nothing,存在 exception 的 if 语句类型返回类型。
object ExceptionSyllabus { def divider(x: Int, y: Int): Float = { if(y == 0) throw new Exception("0 不能作为除数") else x/y } def main(args: Array[String]): Unit = { try { println(divider(10, 0)) } catch { case ex:Exception => println("成功捕获异常" + ex.getMessage) // case ex:Throwable => println("成功捕获异常" + ex.getMessage) } finally {} } }