快学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()
}
时间: 2024-10-08 03:07:51

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

快学scala 第十一章 操作符 读书笔记及习题答案代码

chapter 11 操作符 标签:快学scala 一.笔记 scala种可以在反引号中包含几乎任何字符序列, val 'val' = 42 所有的操作符都是左结合的,除了以冒号(:)结尾的操作符,和赋值操作符.用于构造列表的::操作符是又结合的.1::2::Ni1的意思是1::(2::Ni1),先创建出包含2的列表,这个列表又被作为尾巴拼接到以1作为头部的列表中. 2. 函数调用语法:f(arg1, arg2,...)扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等于f.a

快学Scala第10章----特质

本章要点 类可以实现任意数量的特质 特质可以要求实现它们的类具备特定的字段.方法或超类 和Java接口不同,Scala特质可以提供方法和字段的实现 当你将多个特质叠加在一起时,顺序很重要--其方法先被执行的特质排在更后面 为什么没有多重继承 Scala和Java一样不允许类从多个超类继承:从多了超类继承可能会导致许多问题,例如两个超类有相同的方法,子类该如何使用和菱形继承.在java 中类只能扩展自一个超类,它可以实现任意数量的接口,但接口只能包含抽象方法,不能包含字段. Scala提供了特质(

快学Scala第13章----集合

本章要点 所有集合都扩展自Iterable特质 集合有三大类:序列.集.映射 对于几乎所有集合类,Scala都同时提供了可变的和不可变的版本 Scala列表要么是空的,要么拥有一头一尾,其中尾部本身又是一个列表 集是无先后次序的集合 用LinkedhashSet 来保留插入顺序,或者用SortedSet来按顺序进行迭代 '+' 将元素添加到无先后次序的集合中: +: 和 :+ 向前或向后追加到序列: ++将两个集合串接在一起: -和–移除元素 Iterable和Seq特质有数十个用于常见操作的方

快学Scala第14章----模式匹配和样例类

本章要点 match表达式是一个更好的switch,不会有意外掉入到下一个分支的问题. 如果没有模式能够匹配,会抛出MatchError.可以用case _ 模式来避免. 模式可以包含一个随意定义的条件,称作守卫. 你可以对表达式的类型进行匹配:优先选择模式匹配而不是isInstanceOf/asInstanceOf. 你可以匹配数组.元组和样例类的模式,然后将匹配到的不同部分绑定到变量. 在for表达式中,不能匹配的情况会被安静的跳过. 样例类继承层级中的公共超类应该是sealed的. 用Op

快学Scala 第13章 集合 - 练习解答

1. 编写一个函数,给定字符串,产出一个包含所有字符的下标的映射.举例来说:indexes("Mississippi")应返回一个映射,让'M'对应集{0},'i'对应集{1,4,7,10},依此类推. 使用字符到可变集的映射.另外,你如何保证集是经过排序的? 回答:使用SortedSet可以保证集是经过排序的. package ex13_01 import scala.collection.mutable.SortedSet import scala.collection.mutab

快学Scala 第6章 对象 - 练习

1. 编写一个Conversions对象,加入inchesToCentimeters.gallonsToLiters和milesToKilometers方法. object Conversions {     def main(args: Array[String]){         printf("1 inch = %g centimeters\n", inchesToCentimeters(1))         printf("2 gallons = %g liter

快学Scala 第18章 高级类型 习题解答

1. 实现一个Bug类,对沿着水平线爬行的虫子建模.move方法向当前方向移动,turn方法让虫子转身,show方法打印出当前的位置.让这些方法可以被串接调用.例如: bugsy.move(4).show().move(6).show().turn().move(5).show() 上述代码应显示 4 10 5. package ex18_01 class Bug {   var x = 0   var y = 0   var curr_direction = 0   def move(len:

快学Scala 第16章 XML处理 习题解答

1. <fred/>(0) 得到什么?<fred/>(0)(0)呢?为什么? 回答:<fred/>(0) 得到一个scala.xml.Node,<fred/>(0)(0)也是得到scala.xml.Node. 因为scala.xml.Node 实现了方法 def apply(i: Int): Node,所以支持串接调用. 注意:scala-xml-x.x.x.jar 需要另外导入. scala> val a = <fred/> a: sca

快学Scala 第17章 - 类型参数 习题解答

1. 定义一个不可变类Pair[T,S],带一个swap方法,返回组件交换过位置的新对偶. package ex17_01 object Main extends App {   val p = new Pair(97 -> 'a')   val a = p.swap   println(a) } class Pair[T, S](val p: (T, S)) {   def swap = {     (p._2, p._1)   } } /*output: (a,97) */ 2. 定义一个可