快学Scala第2章–控制结构和函数 笔记
Posted 火影一代
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快学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()
}
以上是关于快学Scala第2章–控制结构和函数 笔记的主要内容,如果未能解决你的问题,请参考以下文章