快学Scala第2章–控制结构和函数 笔记


条件表达式

在Scala中,if/else 表达式是有值的,这个就是跟在if或者else之后的表达式的值。例如:

val s = if(x > 0) 1 else -1
// 类似于
var s = 0
if(x > 0) s = 1 else s = -1

Scala允许使用混合类型的返回值,例如:

if(x > 0) "positive" else -1

 

上式表达式返回的类型是它们类型的公共超类型, 在这里java.lang.String 和 Int 它们的公共超类型是Any。 如果else部分缺失了,例如:

if(x > 0) 1
// 这就等同于
if(x > 0) 1 else ()

这相当于引入了一个Unit类,写做()。你可以把()当做是表示”无有用值”的占位符, 将 Unit 当做是C++或Java的 void。(但是从技术上讲,void没有值,但是Unit是有值的,它表示一个”无值”的值)。
另外,Scala没有switch语句,不过它有强大的模式匹配机制。

小提示:如果想要在REPL中粘贴成块的代码,可以使用粘贴模式,键入 :paste , 把代码块粘贴进去, 然后按下Ctrl + D


语句终止

在C++和Java中, 每个语句都是以分号结尾的,否则不会编译通过,而在Scala中,行尾的位置不需要分号; 但是在单行中写下多个语句的话,就需要将它们用分号隔开。例如:

if(n > 0) { r = r * n; n -= 1}

如果你在写较长的语句需要分两行来写的话,就要确保第一行以一个不能作为结尾的符号结尾, 例如:

s = s0 + (v - v0) * t + // +告诉解析器这里不是语句的结尾
    0.5 * (a - a0) * t * t


块表达式和赋值

在Scala中,{}块包含一系列表达式,其结果也是一个表达式,即块中最后一个表达式的值就是块的值。这样可以直接用块的值初始化val。

val dis={val dx=x=x0;val dy=y-y0;sqrt(dx*dx+dy*dy)}

最后一个表达式的值为Unit,所以整个块的值也就是Unit,而不是n的值。因此 ,赋值操作不能这样用:x=y=1,因为y可能为unit

输入和输出

print/println打印到控制台上。

print("Answer: ")
println(42)
printf("Hello, %s! You are %d years old.\n", "Fred", 42) // C风格格式化字符串

使用 readline从控制台读取一行输入。如果要读取数字、Boolean或者是字符,可以使用readInt、readDouble、readByte、 readShort、readLong、 readFloat、readBoolean、readChar 。

循环

Scala中的while和do循环与C++和Java的相同。但是Scala中没有与for(初始化变量; 检查变量是否满足某条件; 更新变量)循环直接对应的结构。

这样会使 i 遍历右边表达式的所有值。在for循环中没有指定变量的类型,该变量的类型是集合的元素的类型。如果想获得左闭右开的区间,可以使用until方法而不是to方法:

val s = "Hello"
var sum = 0
for (i <- 0 until s.length)
    sum += s(i)
// 等同于
<span style="background-color: rgb(255, 255, 51);">var sum = 0
for (ch <- "Hello")
    sum += ch</span>

注意:在Scala中是没有break或者continue来退出循环的,替代方案有一下几个选项:
1. 使用Boolean型的控制变量
2. 使用嵌套函数—–你可以从函数当中return;
3. 使用Breaks对象中的break方法:

import scala.util.control.Breaks._
breakable {
    for (...) {
        if (...) break;  // 退出breakable块
        ...
    }
}



高级for循环和for推导式

Scala提供了for循环的高级应用:你可以以变量<-表达式的形式提供多个生成器,用分号将它们隔开。例如:

for (i <- 1 to 3; j <- 1 to 3) print ((10 * i + j) + " ")

这里写图片描述

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

for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ")


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

for (i <- 1 to 10) yield i % 3

这里写图片描述

这类循环叫做for推导式。 for推导式生成的集合与它的第一个生成器是类型兼容的。

for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
for (i <- 0 to 1; c <- "Hello") yiels (c + i).toChar

这里写图片描述

对于for循环有多个表达式的话,也可以使用{}将表达式、守卫和定义包含在花括号中,并以换行的方式,而不是分号将它们隔开:

for { i <- 1 to 3
   from = 4 - i
   j <- from to 3
}print((10*i+j)+"  ")//打印13 22 23 31 32 33



函数

在Scala中即支持面向对象编程,又支持函数式编程。因此,Scala除了方外外还支持函数。方法是对对象进行操作,函数不是。C++也有函数,而Java中我们只能用静态方法来模拟。
定义函数需要包括:函数名称、参数和函数体:

def abs(x: Double) = if (x >= 0) x else -x

你必须给出所有函数中参数的类型不过,只要函数不是递归的,你就不需要指定返回类型.
函数体也就是代码块的最后一个表达式就是函数的返回值

def fac(n: Int) = {
    var r = 1
    for (i <- 1 to n) r = r * i
    r
}

对于递归函数:
def fac(n: Int):<span style="background-color: rgb(255, 255, 0);"> Int</span> = if (n <= 0) 1 else n * fac(n - 1)

如果没有返回类型,Scala编译器无法校验n * fac(n - 1)的类型是Int


默认参数和带名参数

和C++、Java样,Scala也提供了默认参数,例如:

def decorate(str: String, left: String = "[", right: String = "]") = left + str +right

decorate(“Hello”) 则输出 [Hello] 。Scala还实现了在调用函数时指定参数名,这样就不必按照函数定义时参数的顺序:decorate(left = “<<<”, str = “Hello”, right = “>>>”) 。带名参数很有用,在C++中,你如果想让right参数使用自己指定的值,那么你就必须也指定left的值,即便是继续使用”[“,因为在C++中默认参数不能让在中间使用,而Scala则可以使用带名参数实现指定某一个参数,例如:decorate(“Hello”, right = “]<<<”) 则会输出 [Hello]<<<


变长参数

Scala中变长参数的实现方式:

def sum(args: Int*) = {
    var result = 0
    for (arg <- args) result += arg
    result
}

这里写图片描述
函数得到的是一个类型为Seq的参数。如果你已经有一个值的序列,则不能直接将它传入上述函数,如果sum函数被调用时传入的是单个参数,那么该参数必须是单个整数,而不是一个整数区间。

val s = sum(1 to 5) // error
val s = sum(1 to 5: _*) // right,将 1 to 5 当做参数序列处理 

对于递归函数:

def recursiveSum(args: Int): Int = {
    if (args.length == 0) 0
    else args.head + recursiveSum(args.tail: _*)
}

注意: 在变长参数中经常使用的一种情况是:参数类型为Object的Java方法,例如PrintStream.printf或者MessageFormat.format时, 需要手工将基本类型进行转换:

val str = MessageFormat.format("The answer to {0} is {1}", "everything", 42.asInstanceOf[AnyRef])


过程

Scala对于不返回值的函数有特殊的表示方法。如果函数体包含在花括号当中但是没有前面的‘=’号, 那么返回类型就是Unit。这样的函数被称作过程。 不过还是建议大家把返回值Unit 加上,方便阅读:

def box(s: String): Unit = {
    ...
}


懒值

在Scala中,当val被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。例如:

<span style="background-color: rgb(255, 255, 51);">lazy</span> val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString

如果程序从不访问words,那么文件也不会被打开。懒值对于开销较大的初始化语句而言十分有用。
说明:懒值是有额外开销的,我们每次访问懒值,都会有一个方法被调用,而这个方法将会以线程安全的方式检查该值是否已被初始化。


异常

Scala异常的工作机制和Java或C++一样。例如:

throw new IllegalArgumentException("x should not be negative")

与Java不同的是,Scala没有“受检”异常—你不需要声明说函数或方法可能会抛出某种异常。throw表达式有特殊的类型Nothing。这在if/else的表达式中很有用,如果一个分支的类型是Nothing,那么if/else表达式的类型就是另一个分支的类型,例如:

if (x >= 0) {
    sqrt(x)
} else {
    throw new IllegalArgumentException("x should not be negative")
}

则该if/else表达式的类型是Double。

捕获异常的语法采用的是模式匹配的语法,在后面会介绍,例如:

try {
    process(new URL("http://horstmann.com/fred-tiny.gif"))
} catch {
   case _: MalformedURLExeception => println("Bad URL: " + url)
   case ex: IOException => ex.printStackTrace()
}

和C++、Java一样,更通用的异常应该排在更具体的异常之后。注意,如果不需要使用捕获的异常对象,可以使用_来替代变量名。
try/finally语句可以让你释放资源,不论有没有发生异常,例如:

var in = new URL("http://horstmann.com/fred-tiny.gif").openStream()
try {
    process(in)
} finally {
    in.close()
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值