Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1523993
  • 博文数量: 465
  • 博客积分: 8915
  • 博客等级: 中将
  • 技术积分: 6365
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-30 15:05
文章分类

全部博文(465)

文章存档

2017年(33)

2016年(2)

2015年(4)

2014年(29)

2013年(71)

2012年(148)

2011年(178)

分类: Java

2012-11-06 09:01:25

条件表达式

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

if (x > 0) 1 else -1

上述表达式的值是1或−1,具体是哪一个取决于x的值。你可以将if/else表达式的值赋值给变量:

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

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

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

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

(之前已经提过,Scala中的分号绝大多数情况下不是必需的。)

Java和C++有一个?:操作符用于同样目的。如下表达式

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

等同于Scala表达式 if (x > 0) 1 else −1。不过,你不能在?:表达式中插入语句。Scala的if/else将在Java和C++中分开的两个语法结构if/else和?:结合在了一起。

在Scala中,每个表达式都有一个类型。举例来说,表达式 if (x > 0) 1 else −1的类型是Int,因为两个分支的类型都是Int。混合类型表达式,比如:

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

上述表达式的类型是两个分支类型的公共超类型。在本例中,其中一个分支是java.lang.String,而另一个分支是Int。它们的公共超类型叫做Any。(详细内容参见8.11节。)

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

if (x > 0) 1

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

if (x > 0) 1 else ()

你可以把()当做是表示“无有用值”的占位符,将Unit当做Java或C++中的void。(从技术上讲,void没有值但是Unit有一个表示“无值”的值。如果你一定要深究的话,这就好比空的钱包和里面有一张写着“没钱”的无面值钞票的钱包之间的区别。)

说明:Scala没有switch语句,不过它有一个强大得多的模式匹配机制,我们将在第14章中看到。在现阶段,用一系列的if语句就好。

注意: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中才会有这个顾虑。在被编译的程序中,解析器会找到下一行的else。

提示:如果你想在REPL中粘贴成块的代码,而又不想担心REPL的近视问题,可以使用粘贴模式。键入:

:paste

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

语句终止

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

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

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

我们需要用分号将 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++的程序员一开始并不适应省去分号的做法。如果你倾向于使用分号,用就是了——它们没啥坏处。

块表达式和赋值

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

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

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

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

{ }块的值取其最后一个表达式,在此处以粗体标出。变量dx和dy仅作为计算所需要的中间值,很干净地对程序其他部分而言不可见了。

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

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

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

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

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

x = y = 1 // 别这样做

y = 1的值是(),你几乎不太可能想把一个Unit类型的值赋值给x。(与此相对应,在Java和C++中,赋值语句的值是被赋的那个值。在这些语言中,将赋值语句串接在一起是有意义的。)

输入和输出

如果要打印一个值,我们用print或println函数。后者在打印完内容后会追加一个换行符。举例来说,

print("Answer: ")

println(42)

与下面的代码输出的内容相同:

println("Answer: " + 42)

另外,还有一个带有C风格格式化字符串的printf函数:

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

你可以用readLine函数从控制台读取一行输入。如果要读取数字、Boolean或者是字符,可以用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, your will be %d.\n", name, age + 1)

本文节选自《快学Scala

电子工业出版社出版

(美)霍斯曼(Horstmann,C.S.)著

高宇翔译

 

阅读(692) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~