控制结构和函数

1.在Scala中,几乎所有构造出来的语法结构都有值。这个特性是为了使得程序更加精简,也更易读。

  (1)if表达式有值

  (2)块也有值——是它最后一个表达式的值

  (3)Scala的for循环就像是“增强版”的Java for循环

  (4)分号(在绝大多数情况下)不是必须的

  (5)void类型是Unit

  (6)避免在函数定义中使用return

  (7)注意别在函数式定义中漏掉了=

  (8)异常的工作方式和Java中基本一样,不同的是catch语句中使用“模式匹配”

  (9)Scala没有受检异常

2.条件表达式

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

    if(x > 0) 1 else -1

  可以将if/else表达式的值赋值给变量:

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

  这与如下语句的效果一样:

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

  不过第一种写法给号,因为它可以用来初始化一个val;而第二种写法中,s必须是var

  Scala中每个表达式都有一个类型。例如,表达式 if(x > 0) 1 else -1的类型是Int,因为两个分支的类型都是Int。混合类型表达式,比如if(x > 0) "positive" else -1,上述表达式的类型是另个分支类型的公共超类型。java.lang.String和Int的公共超类型叫做Any。

  如果else部分缺失了,比如:if(x > 0) 1,那么有可能if语句没有输出值。但是在Scala中,每个表达式都应该有某种值。解决方案是引入一个Unit类,写作()。不带else的if语句等同于:if(x > 0) 1 else ()

  把()当做表示“无有用值”的占位符,将Unit当做Java中的void。

  Scala没有switch语句,不过它有一个强大得多的模式匹配机制。现阶段,用一系列的if语句就好。

3.REPL比起编译器来更加“近视”——它在同一时间只能看到一行代码。可用花括号:

  if(x > 0)  {1

  } else if(x == 0) 0 else -1

只有在REPL中才会有这个顾虑。在REPL中粘贴成块代码,可用使用粘贴模式。键入:       :paste  把代码块粘贴进去,然后按下Ctrl+D。这样REPL就会把代码块当做一个整体来分析。

4.语句终止

  在Scala中——与JavaScript和其他脚本语言类似——行尾的位置不需要分号。不过,如果你想要在单行中写下多个语句,就需要将它们以分号隔开。例如:

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

  如果写较长的语句,需要分两行来写,就要确保第一行以一个不能用来做语句结尾的符号结尾。通常来说一个比较好的选择是操作符:

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

      0.5 * (a - a0) * t * t

   如果倾向于使用分号,用就是了——他们没啥坏处。

5.块表达式和赋值

  在Java中,语句块是一个包含于{}中的语句序列。在Scala中,{}快包含一系列表达式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。

  这个特性对于val的初始化需要很多步完成的情况很有用。例如:

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

  在Scala中,赋值动作本身是没有值的——或者,更严格地说,它们的值是Unit类型的。

    一个以赋值语句结束的块,比如{r = r * n; n-=1 }的值是Unit类型的。当我们定义函数时需要意识到这一点。

6.输入和输出

  如果要打印一个值,用print或者println函数。另外,还有一个带有C风格格式化字符串的printf函数:

    printf("Hello",  %s! You are %d old. \n", "Fred", 42) //  Hello,  Fred! You are 42 old.

  用readLine函数从控制台读取一行输入。也可以用readInt、readDouble、readByte、readShort、readLong、readFloat、readBoolean、readChar。不过,readLine带一个参数作为提示字符串:

    val name = readLine(”Your name:”)

    print(“Your age: ”)

    val age = readInt()

    printf("Hello, %s! Next Year, you will be %d .\n, name, age + 1)

7.循环

  Scala拥有与Java和C++相同的while和do循环。例如:

    while(n > 0) {

      r = r * n;

      n -=1;

    }

  Scala没有与for(初始化变量;检查变量是否满足条件;更新变量)循环直接对应的结构。如果需要这样的循环,有两种选择:一是使用while循环,二是使用如下for语句:

    for(i  <-  表达式)  让变量i遍历 <- 右边的表达式的所有值。至于这个遍历具体如何执行,则取决于表达式的类型。对应Range而言,这个循环会让i一次取得区间中的每个值。

  例如:

    for(i <- 1 to n)

      r = r * i

  遍历数组和字符串时,需要0到n-1的区间,可以使用until方法而不是to方法。util方法返回一个并不包含上限的区间。

    val s = "Hello"

    var sum = 0

for(i <- 0 util  s.length)  //[0,s.length-1]

Scala中并没有提供break或continue语句来退出循环。如果需要break语句:

  (1)使用Boolean型的控制变量

   (2)使用嵌套函数——从函数当中return

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

    import scala.util.control.Breaks._

    breakable {

      for(…) {

      if(...) break;

      ...

      }

    }

这里,控制权的转移是通过抛出和捕获异常完成的,因此,如果时间很重要的话,尽量避免使用这套机制。

8. 高级for循环和for推导式

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

    for(i <- 1 to 3; j <- 1 to 3) print ((10 * i + j) + " ") //打印11 12 13 21 22 23 31 32 33

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

    for(i <- 1 to 3; j <- 1 to 3  if  i != j) print((10 * i + j) + " ") //打印12 13 21 23 31 32

  注意if之前并没有分号

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

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

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

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

    //生成 Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)

这类循环叫for推导式

for推到式生成的集合与它的第一个生成器是类型兼容的。

  for(c <- "Hello"; i <- 0 to 1) yield (c + i).toChar  //将生成"HIeflmlmop"

  for(i <- 0 to 1; c <- "Hello") yield (c + i).toChar //将生成Vector('H', 'e', 'l', 'l', 'o', 'I', 'f', 'm', 'm', 'p')

  也可以将生成器、守卫和定义包含在花括号中,并可以一换行的方式而不是分号来分隔它们:

    for{ i <- 1 to 3

      from = 4 - i

      j <- from to 3 }

9. 函数

  Scala除了方法还支持函数。方法针对对象进行操作,函数不是。不过在Java中我们只能使用静态方法来模拟。

  定义函数,给出函数的名称,参数和函数体,例如:

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

    必须给出所有参数的类型只要函数不是递归的,就不用指定返回值类型。Scala编译器可以通过=符号右侧的表达式推断出返回值类型。

  如果函数体需要多个表达式完成,可以用代码块。块中最后一个表达式的值就是函数的返回值。例如:

    def  fac(n : Int) = {

      var r = 1

      for(i <- 1 to n) r = r * i

      r     //返回r的值

    }

  对于递归函数,必须制定返回值类型。例如:

    def fac(n : Int) : Int = if(n<=0) 1 else n * fac(n - 1)

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

10.默认参数和带名参数

  在调用某些函数时并不显式地给出所有的参数值,对于这些函数可以使用默认参数。例如,

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

      left + str + right

  这个函数的两个参数left和right带有默认值 "[" 和 "]"。调用 decorate("hello")得到"[hello]"。

  如果想对参数的数量,你给出的值不够,默认参数会从后往前逐个应用进来。例如,decorate("hello",">>[")会使用right参数的默认值。

  可以在提供参数值的时候指定参数名,例如:decorate(left=“《《”,str = “hello”,right=“》》”),结果是"《《hello》》"。注意,带名参数并不需要跟参数列表的顺序完全一致。带名参数使得函数更加可读。

  可以混用未命名参数和带名参数,只要那些未命名的参数是排在前面的即可。

    decorate("hello", right = "}>>" //将调用decorate("hello", "[", "}>>")

11.变长参数

  实现一个可以接受可变长度参数列表的函数会更方便。

    def  sum(args: Int*) = {

      var  result = 0

      for(arg <- args)  result += arg

      result

    }

 可以使用任意多的参数来调用该函数: val s = sum(1, 4, 9 ,16, 25)

 如果已有一个值的序列,则不能将它传入上述函数。例如:val s = sum(1 to 5)  //错误

 如果sum函数被调用传入的是单个参数,那么该参数必须是单个整数,而不是一个整数区间。解决方案:告诉编译器你希望这个参数被当做参数序列处理。追加    :_*

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

快学Scala 2的更多相关文章

  1. 快学Scala习题解答—第一章 基础

    1 简介 近期对Scala比较感兴趣,买了本<快学Scala>,感觉不错.比<Programming Scala:Tackle Multi-Core Complexity on th ...

  2. 《快学Scala》

    Robert Peng's Blog - https://mr-dai.github.io/ <快学Scala>Intro与第1章 - https://mr-dai.github.io/S ...

  3. 快学Scala 第十九课 (trait的abstract override使用)

    trait的abstract override使用: 当我看到abstract override介绍的时候也是一脸懵逼,因为快学scala,只介绍了因为TimestampLogger中调用的super ...

  4. [Scala] 快学Scala A3L3

    Actor 通过尽可能避免锁和共享状态,actor使得我们能够容易地设计出正确.没有死锁或争用状况的程序. Scala类库提供了一个actor模型的简单实现.AKKA是更高级的actor类库. 19. ...

  5. [Scala] 快学Scala A2L2

    集合 13.1 集合的三大类 所有的集合都扩展Iterable特质.集合的三大集合为Seq, Set, Map Seq是一个有先后次序的值的序列,比如数组或列表.IndexSeq允许我们通过整型下表快 ...

  6. [Scala] 快学Scala A1L1

    基础 1.1 声明值和变量 在Scala中,鼓励使用val; 不需要给出值或变量的类型,这个信息可以从初始化表达式推断出来.在必要的时候,可以指定类型. 在Scala中,仅当同一行代码中存在多条语句时 ...

  7. 快学Scala第一部分

    转载: 1.变量声明 val answer = 8 * 5 + 2; //常量 var counter = 0;    //变量 //在必要的时候 ,可以指定类型 val greeting:Strin ...

  8. 《快学Scala》——数组、映射和元组

    数组 定长数组:在Scala中可以用Array,初始化一个定长数组.例如: val nums = new Array[Int](10) //10个整数的数组,所有元素初始化为0 val a = new ...

  9. 《快学Scala》——控制结构和函数

    条件表达式 在Scala中if/else表达式有值,这个值就是跟在if或else之后的表达式的值.例如: if (x > 0) 1 else -1 上述表达式的值是1或-1,具体是哪一个取决于x ...

随机推荐

  1. [Python] 当猎头遇上 Guido van Rossum

    Guido van Rossum 收到猎头的邀请函和他的回复. 猎头 你好,Guido! 我在 Google 搜索中无意间看见你的简历.看起来你精通 Python.我非常愉快能够得到你的回复并了解你的 ...

  2. 一款Redis客户端,可以作为Redis Desktop manager的有效补充或替代

    一.由来 对于redis客户端,我和大多数人一样,都是用Redis Desktop Manager. 但我发现个问题,我的版本是0.9.1.771. 我这个版本有个问题,就是如果value太长的话,不 ...

  3. Jenkins和Sonar集成

    Jenkins可以通过插件的形式和Sonar很好的集成. (1)Jenkin安装Sonar插件(这里我估计安装的插件有点多) 注意:之前安装Jenkins的时候我用的是JDK系统环境环境变量jdk1. ...

  4. Linux下的tr编辑器命令详解

    通过使用 tr,您可以非常容易地实现 sed 的许多最基本功能.您可以将 tr 看作为 sed 的(极其)简化的变体:它可以用一个字符来替换另一个字符,或者可以完全除去一些字符.您也可以用它来除去重复 ...

  5. db first和code first

    1. db first 是现有数据库,再写代码.根据数据库的表生成类. django里面:python manage.py  inspectdb 2. code first 是先写代码,后创建数据库. ...

  6. A - ACM Rank Table

    ACM contests, like the one you are participating in, are hosted by the special software. That softwa ...

  7. web -- Navigator.vibrate(); 使设备(有振动硬件)产生有频率的振动

    MDN 文档 此方法需要用户手势. 否则,它返回false. const koa2 = require(`koa2`); const Router = require(`koa-router`); c ...

  8. html5__Notifications API 桌面通知

    MDN地址 google 文档 https://developers.google.cn/web/fundamentals/push-notifications/ const koa2 = requi ...

  9. Linux环境变量与文件查找

    作业: 找出/etc目录下所有以.list结尾的文件 代码:locate /etc/\*.list sudo find /etc/ -name \*.list

  10. ERP项目实施记录04

    周二做了计划部门的需求调查,提到现有计划(一天计划)的准确率仅有60~70%,每天下来都有30%~40%不能达成. 计划部门提出的需求更多是基于Excel操作思路,要求未来的系统要有更多的" ...