if表达式
Scala的if如同许多其它语言中的一样工作。它测试一个状态并据其是否为真,执行两个分支中的一个。下面是一个常见的例子,以指令式风格编写:
51CTO编辑推荐:Scala编程语言专题
这段代码声明了一个变量,filename,并初始化为缺省值。然后使用if表达式检查是否提供给程序了任何参数。如果是,就把变量改成定义在参数列表中的值。如果没有参数,就任由变量设定为缺省值。
- var filename = "default.txt"
- if (!args.isEmpty)
- filename = args(0)
这段代码可以写得更好一点,因为就像第2章第三步提到过的,Scala的if是能返回值的表达式。代码7.1展示了如何不使用任何var而实现前面一个例子同样的效果:
- val filename =
- if (!args.isEmpty) args(0)
- else "default.txt"
代码 7.1 在Scala里根据条件做初始化的惯例
这一次,if有了两个分支。如果args不为空,那么初始化元素,args(0),被选中。否则,缺省值被选中。这个if表达式产生了被选中的值,然后filename变量被初始化为这个值。这段代码更短一点儿,不过它的实际优点在于使用了val而不是var。使用val是函数式的风格,并能以差不多与Java的final变量同样的方式帮到你。它让代码的读者确信这个变量将永不改变,节省了他们扫描变量字段的所有代码以检查它是否改变的工作。
使用val而不是var的第二点好处是他能更好地支持等效推论:equational reasoning。在表达式没有副作用的前提下,引入的变量等效于计算它的表达式。因此,无论何时都可以用表达式替代变量名。如,要替代println(filename),你可以这么写:
- println(if (!args.isEmpty) args(0) else "default.txt")
选择权在你。怎么写都行。使用val可以帮你安全地执行这类重构以不断革新你的代码。
尽可能寻找使用val的机会。它们能让你的代码既容易阅读又容易重构。
while循环
Scala的while循环表现的和在其它语言中一样。包括一个状态和循环体,只要状态为真,循环体就一遍遍被执行。代码7.2展示了一个例子:
- def gcdLoop(x: Long, y: Long): Long = {
- var a = x
- var b = y
- while (a != 0) {
- val temp = a
- a = b % a
- b = temp
- }
- b
- }
代码 7.2 用while循环计算***公约数
Scala也有do-while循环。除了把状态测试从前面移到后面之外,与while循环没有区别。代码7.3展示了使用do-while反馈从标准输入读入的行记录直到读入空行为止的Scala脚本:
- var line = ""
- do {
- line = readLine()
- println("Read: " + line)
- } while (line != null)
代码 7.3 用do-while从标准输入读取信息
while和do-while结构被称为“循环”,不是表达式,因为它们不产生有意义的结果,结果的类型是Unit。说明产生的值(并且实际上是唯一的值)的类型为Unit。被称为unit value,写做()。()的存在是Scala的Unit不同于Java的void的地方。请在解释器里尝试下列代码:
- scala> def greet() { println("hi") }
- greet: ()Unit
- scala> greet() == ()
- hi
- res0: Boolean = true
由于方法体之前没有等号,greet被定义为结果类型为Unit的过程。因此,greet返回unit值,()。这被下一行确证:比较greet的结果和unit值,(),的相等性,产生true。
另一个产生unit值的与此相关的架构,是对var的再赋值。比如,假设尝试用下面的从Java(或者C或C++)里的while循环成例在Scala里读取一行记录,你就遇到麻烦了:
- var line = ""
- while ((line = readLine()) != "") // 不起作用
- println("Read: "+ line)
编译这段代码时,Scala会警告你使用!=比较类型为Unit和String的值将永远产生true。而在Java里,赋值语句可以返回被赋予的那个值,同样情况下标准输入返回的一条记录在Scala的赋值语句中永远产生unit值,()。因此,赋值语句“line = readLine()”的值将永远是()而不是""。结果,这个while循环的状态将永远不会是假,于是循环将因此永远不会结束。
由于while循环不产生值,它它经常被纯函数式语言所舍弃。这种语言只有表达式,没有循环。虽然如此,Scala仍然包含了while循环,因为有些时候指令式的解决方案更可读,尤其是对那些以指令式背景为主导的程序员来说。例如,如果你想做一段重复某进程直到某些状态改变的算法代码,while循环可以直接地表达而函数式的替代者,大概要用递归实现,或许对某些代码的读者来说就不是那么显而易见的了。
如,代码7.4展示了计算两个数的***公约数的替代方式。 给定同样的值x和y,代码7.4展示的gcd函数将返回与代码7.2中gcdLoop函数同样的结果。这两种方式的不同在于gcdLoop写成了指令式风格,使用了var和while循环,而gcd更函数式风格,采用了递归(gcd调用自身)并且不需要var:
- def gcd(x: Long, y: Long): Long =
- if (y == 0) x else gcd(y, x % y)
代码 7.4 使用递归计算***公约数
通常意义上,我们建议你如质疑var那样质疑你代码中的while循环。实际上,while循环和var经常是结对出现的。因为while循环不产生值,为了让你的程序有任何改变,while循环通常不是更新var就是执行I/O。可以在之前的gcdLoop例子里看到。在while循环工作的时候,更新了a和b两个var。因此,我们建议你在代码中对while循环抱有更怀疑的态度。如果没有对特定的while或do循环较好的决断,请尝试找到不用它们也能做同样事情的方式。
【相关阅读】