Scala学习(二)控制结构和函数

1.条件表达式

Scala的if/else语法结构和Java一样。不过,在Scala中if/else表达式有值,这个值就是跟在if或else之后的表达式的值。例如:

if(x>0) 1 else -1

或者将表达式的值赋值给变量:

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

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

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

不过,第一种写法更好,因为它可以用来初始化一个val常量。而第二种写法中,s必须是var变量。

--------------------------------

在Scala中if/else有着和Java或者C++的三元表达式相同的功能,例如

x>0 ? 1 : -1 //Java或C++

而在Scala中,我们可以这样:

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

在Scala中每个表达式都有一种类型,表达式的类型可以相同可以不相同,这点与Java有所不同。不同类型表达式的if/else我们称为混合表达式,

接收他们的值或变量的类型为公共类型Any(相当于Java中的Object)

--------------------------------

如果else部分缺失了,例如:

if(x>0) 1

那么可能if没有输出值,但是在Scala中,每个表达式都应该有值。这个问题的解决方案是引入一个Unit类,写做()。不带饿死了的这个if语句等同于

if(x>0) 1 else ()

你可以把()当做“无有用值”的占位符,将Unit当做Java或C++中的void。

注:Scala没有switch语句

注:REPL(解析器)比起编译器来说,它只能够按行执行程序,例如:

if (x > 0) 1 

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

这时REPL会执行if (x>0) 1 然后显示结果。之后的else他将会报错。

如果你想在else前换行的话,要用花括号:

if (x > 0) {1 

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

只有在REPL中才会有这样的顾虑,其他地方大家大可不必担心

提示:如果想在REPL中粘贴成块的代码又不想让REPL按行执行,可以使用粘贴模式。键入

:paste

把代码粘贴进去,然后Ctrl+D。这样REPL就会把代码块当做一个整体分析。

2.语句终止

在Java和C++中,每个语句都要以分号结束。而现在Scala中与JavaScript和其他脚本类似,行位不需要设置分号。同样在}、else以及类似的位置也不必写分号,只要能够从上下文明确的判断出这里语句是终止的即可。

不过,如果你想在单行中写下多个语句,就需要将他们以分号分开。例如:

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

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

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

0.5 * (a - a0) * t * t

在实际的编码时,长表达式通常涉及函数或方法调用,Scala程序猿更倾向于使用Kernighan&Ritchie风格的花括号

if(n >0){

  r = r * n

  n -= 1

}

以‘{’结束的行很清楚的表达了后面还有内容,直到遇见匹配的‘}’

许多来自Java或C++的程序员一开始并不适应省去分号的做法。如果你更倾向于使用分号,用了就是了,没啥坏处。

3.块表达式和赋值

在Java或C++中,块语句是包含于{}中的语句序列。每当你需要在逻辑分支或循环中放置多个动作时,你都可以使用块语句。

在Scala中,{}块包含一系列的表达式,其中结果也是一个表达式。块中最后一个表达式的值就是块儿的值。

--------------------------------

在Scala中,赋值动作本身是没有值得——或者,更严格地说,他们的值是Unit类型的。你应该记得,Unit类型等同于Java和C++中的void,而这个类型的值只有一个值,写做()。

一个赋值语句结束的块,比如

{r = r * n; n -= 1}

的值是Unit类型的。这没有问题,只是当我们定义函数时需要意识到这一点。

注:由于赋值语句的值是Unit类型的,别把他们串接在一起。

x = y = 1 //别这么做

y = 1 的值是(),你不可能想把一个Unit类型的值赋值给x。(在Java或者C++中,上面的做法是可以做到x、y同时赋值的)

4.输入和输出

如果要打印一个值,我们用print或者println以及printf函数。与Java中的用法相同,这里不做过多解释。

--------------------------------

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

与其他方法不同,readLine带一个参数作为提示字符串:

image2018-9-29_11-25-22.png?version=1&modificationDate=1538191519000&api=v2

image2018-9-29_11-25-48.png?version=1&modificationDate=1538191545000&api=v2

 

5.循环

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

image2018-9-29_11-38-55.png?version=1&modificationDate=1538192332000&api=v2

--------------------------------

Scala中没有与Java相同的for结构:for(初始化变量;检查变量是否满足条件;更新变量)。

在Scala中只有这样的for循环语法:

for(i ← 表达式)      //i不需要定义,也可以所以取名

接下来我们用RichInt类的to方法,去演示Scala中for的用法:

image2018-9-29_11-48-5.png?version=1&modificationDate=1538192882000&api=v2

还有另一种遍历方法until,它常用于遍历字符串或者数组,同上面的to方法的例子比较,能看出他们两个的区别

image2018-9-29_11-51-8.png?version=1&modificationDate=1538193066000&api=v2

to方法:包含头和尾的遍历(相当于<=)

until方法:包含头不包含尾的遍历(相当于<)

注:遍历字符串的两种方法:

1.用until方法

image2018-9-29_11-59-11.png?version=1&modificationDate=1538193548000&api=v2

2.直接遍历

image2018-9-29_11-59-34.png?version=1&modificationDate=1538193571000&api=v2

--------------------------------

Scala中并没有提供break或者continue语句来退出循环。如果需要break时我们可以采取如下选项:

  1. 使用Boolean型控制变量
  2. 使用嵌套函数——你可以从函数当中return
  3. 使用Breaks对象中的break方法

break方法使用示例:

image2018-9-29_13-40-5.png?version=1&modificationDate=1538199602000&api=v2

 

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

Scala中的for循环要比Java功能丰富的多,比如:

1.我们可以在for循环中添加多个 变量←表达式 这种形式的生成器,用分号分隔开:

image2018-9-29_13-51-22.png?version=1&modificationDate=1538200280000&api=v2

2.每一个生成器都可以带一个守卫,以if开头的Boolean表达式:注意if前没有分号

image2018-9-29_13-51-43.png?version=1&modificationDate=1538200300000&api=v2

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

image2018-9-29_13-53-59.png?version=1&modificationDate=1538200436000&api=v2

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

image2018-9-29_13-58-58.png?version=1&modificationDate=1538200736000&api=v2

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

image2018-9-29_14-7-4.png?version=1&modificationDate=1538201228000&api=v2

image2018-9-29_14-41-50.png?version=1&modificationDate=1538203308000&api=v2

7.函数

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

要定义函数,完整格式如下:

def  [函数名称]([参数名称]:[参数类型]...): [返回值类型] = {

    函数体

}

例如:

def add(x : Int , y : Int) : Int = {

    var z = x + y;

    return z;

}

由于{}块中最后一个表达式的值就是块儿的值。因此我们可以省略return。

def add(x : Int , y : Int) : Int = {

    var z = x + y;

    z;

}

在进一步简化:

def add(x : Int , y : Int) : Int = x + y

在Scala中,只要函数不是递归的,我们就不需要指定返回类型。Scala编译器可以通过 = 右侧的表达式类型推断出返回值类型。因此最终函数简化如下:

def add(x : Int , y : Int)  = x + y

如果是递归函数,我们就必须要指定返回值类型了。

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

说明:虽然在带名函数中使用return并没有什么不对,但是我们最好适应没有return的日子。很快,你就会使用大量的匿名函数,这些函数中return并不返回值给调用者。

它跳出到包含它的带名函数中。我们可以把return当做是函数版break语句,尽在需要时使用。

8.默认参数和带名参数

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

def decorate(prefix : String, str : String, suffix : String) = {

    prefix + str + suffix

}

在调用上面的decorate函数的时候我们必须给出所有的参数,否则函数将会执行错误,那如果此时我们只想给出一个参数就能让其运行我们该如何做呢?那就是默认参数值:

def decorate(prefix : String = "[[", str : String, suffix : String = "]]") = {

    prefix + str + suffix

}

我们可以在定义函数入参时先给其赋值,从而做到默认参数值的功能,但是另一个问题又来了,作为程序员我们都知道调用方法或者函数的时候,参数都是按顺序写入的,我们如何做到给指定位置的入参赋值呢?Scala帮我们做到了这一点:

image2018-9-29_15-52-57.png?version=1&modificationDate=1538207575000&api=v2

如图,我们可以向表达式一样为指定参数赋值,这样我们就可以无视参数的顺序来调用函数或者方法了,这就是带名参数

9.变长参数

我们知道在Java中,边长参数用 ... 来表示:

public void method(int a ...){

    方法体...

}

而在Scala中,边长参数用* 来表示,比如我们创建一个计算多个数相加的方法,参数不确定。

def add(a : Int*) = {

    var sum = 0;

    for(x <- a) sum += x;

    sum;

}

image2018-9-29_16-2-20.png?version=1&modificationDate=1538208138000&api=v2

对于上面的add方法,如果你已经有了一个有值序列,则不能直接将它传入上面的大的方法,比如:

val a = add(1 to 5)  //错误

因为我们的add方法的入参是Int类型的序列,而1 to 5 的类型为:

image2018-9-29_16-6-51.png?version=1&modificationDate=1538208408000&api=v2

他并不是add方法的入参类型因此直接传入一定有问题,需要进行类型的转换,那么范围类型如何转换成入参序列呢?Scala中为我们提供了一种语法,追加 :_*

val a = add(1 to 5 :_* )

两种情况对比:

image2018-9-29_16-9-40.png?version=1&modificationDate=1538208578000&api=v2

在递归的定义中我们会用到上述语法:

def recursiveSum(args : Int*): Int = {

    if(args.length == 0) 0

    else args.head = recursiveSum(args.tail:_*)

}

在这里,序列的head是他的首个元素,而tail是所有其他元素的序列,这又是一个序列,我们用 :_* 来将它转换成参数序列。

10.过程

Scala对于不返回值得函数有特殊的表示法。如果函数体包含在花括号当中但没有前面的=号,那么返回类型就是Unit。这样的函数被称为过程。过程不返回值,我们调用它仅仅是为了他的辅助作用。

例如:

def out(a : Int){

    print(a)

}

或者我们可以显示声明Unit返回类型:

def out(a : Int): Unit = {

    print(a)

}

11.懒值

当val 被声明为lazy时,他的初始化将被延迟,也就是懒加载,知道我们首次对它进行取值,例如

image2018-9-29_16-34-3.png?version=1&modificationDate=1538210041000&api=v2

//在words被定义时取值

val words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString

//在words被首次使用时取值

lazy val words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString

//在每一次words被使用时取值

def words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString

你可以把懒值当做是介于val和def的中间状态

--------------------------------

说明:懒值并不是没有额外开销。我们每次访问懒值,都会有一个方法被调用,而这个方法将会以线程安全的方式检查该值是否已被初始化。

12.异常

Scala的异常工作机制和Java或者C++一样。抛出异常时你可以这样:

throw new Exception("this is a exception")

和Java一样,排出的对象必须是java.lang.Throwable的子类。不过,与Java不同的是,Scala没有“受检”异常,你不需要声明说函数或方法可能抛出某种异常。

--------------------------------

throw表达式有特殊的返回类型Nothing。比如:

if(x >= 0){

    sqrt(x)

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

第一个分支类型为Double,第二个分支类型为Nothing。因此,if/else表达式的类型是Double

--------------------------------

捕获异常的语法采用的是模式匹配的语法

try{

    "Hello".toInt

} catch {

    case _ : Exception       => println("xxxxxxx")

    case ex : IOException => ex.printStackTrace()

}

和Java或C++一样,更通用的异常应该排在更具体的异常之后。

如果你不需要使用捕获的异常对象,可以使用_来代替变量名。

--------------------------------

try/finally语句让你可以释放资源,不论有没有发生异常。这点和Java一样。

我们也可以下车和Java中一样的try/catch/finally语句:

try{...}catch{...}finally{...}

转载于:https://my.oschina.net/u/3687664/blog/2221905

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值