Scala中的if表达式和while循环

开发 后端
本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第七章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

if表达式

Scala的if如同许多其它语言中的一样工作。它测试一个状态并据其是否为真,执行两个分支中的一个。下面是一个常见的例子,以指令式风格编写:

51CTO编辑推荐:Scala编程语言专题

  1. var filename = "default.txt" 
  2. if (!args.isEmpty)  
  3.  filename = args(0)  
这段代码声明了一个变量,filename,并初始化为缺省值。然后使用if表达式检查是否提供给程序了任何参数。如果是,就把变量改成定义在参数列表中的值。如果没有参数,就任由变量设定为缺省值。

这段代码可以写得更好一点,因为就像第2章第三步提到过的,Scala的if是能返回值的表达式。代码7.1展示了如何不使用任何var而实现前面一个例子同样的效果:

  1. val filename =  
  2.  if (!args.isEmpty) args(0)  
  3.  else "default.txt" 

代码 7.1 在Scala里根据条件做初始化的惯例

这一次,if有了两个分支。如果args不为空,那么初始化元素,args(0),被选中。否则,缺省值被选中。这个if表达式产生了被选中的值,然后filename变量被初始化为这个值。这段代码更短一点儿,不过它的实际优点在于使用了val而不是var。使用val是函数式的风格,并能以差不多与Java的final变量同样的方式帮到你。它让代码的读者确信这个变量将永不改变,节省了他们扫描变量字段的所有代码以检查它是否改变的工作。

使用val而不是var的第二点好处是他能更好地支持等效推论:equational reasoning。在表达式没有副作用的前提下,引入的变量等效于计算它的表达式。因此,无论何时都可以用表达式替代变量名。如,要替代println(filename),你可以这么写:

  1. println(if (!args.isEmpty) args(0else "default.txt"

选择权在你。怎么写都行。使用val可以帮你安全地执行这类重构以不断革新你的代码。

尽可能寻找使用val的机会。它们能让你的代码既容易阅读又容易重构。

while循环

Scala的while循环表现的和在其它语言中一样。包括一个状态和循环体,只要状态为真,循环体就一遍遍被执行。代码7.2展示了一个例子:

  1. def gcdLoop(x: Long, y: Long): Long = {  
  2.  var a = x  
  3.  var b = y  
  4.  while (a != 0) {  
  5.   val temp = a  
  6.   a = b % a  
  7.   b = temp  
  8.  }  
  9.  b  

代码 7.2 用while循环计算***公约数

Scala也有do-while循环。除了把状态测试从前面移到后面之外,与while循环没有区别。代码7.3展示了使用do-while反馈从标准输入读入的行记录直到读入空行为止的Scala脚本:

  1. var line = "" 
  2. do {  
  3.  line = readLine()  
  4.  println("Read: " + line)  
  5. while (line != null)  

代码 7.3 用do-while从标准输入读取信息

while和do-while结构被称为“循环”,不是表达式,因为它们不产生有意义的结果,结果的类型是Unit。说明产生的值(并且实际上是唯一的值)的类型为Unit。被称为unit value,写做()。()的存在是Scala的Unit不同于Java的void的地方。请在解释器里尝试下列代码:

  1. scala> def greet() { println("hi") }  
  2. greet: ()Unit  
  3. scala> greet() == ()  
  4. hi  
  5. res0: Boolean = true 

由于方法体之前没有等号,greet被定义为结果类型为Unit的过程。因此,greet返回unit值,()。这被下一行确证:比较greet的结果和unit值,(),的相等性,产生true。

另一个产生unit值的与此相关的架构,是对var的再赋值。比如,假设尝试用下面的从Java(或者C或C++)里的while循环成例在Scala里读取一行记录,你就遇到麻烦了:

  1. var line = "" 
  2. while ((line = readLine()) != ""// 不起作用  
  3.  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:

  1. def gcd(x: Long, y: Long): Long =  
  2.  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循环较好的决断,请尝试找到不用它们也能做同样事情的方式。

【相关阅读】

  1. 学习Scala的重载方法和隐式转换
  2. Scala的四种标识符构成方式
  3. Scala的私有字段和定义操作符
  4. Scala的从构造器:主构造器之外的构造器
  5. 在Scala中检查先决条件、添加字段和自指向

责任编辑:book05 来源: Artima
相关推荐

2012-07-18 09:45:32

Java 8ScalaLambda

2009-07-21 14:38:08

Scalamatch表达式break和conti

2009-07-21 14:16:18

Scalafor表达式

2024-01-05 17:41:36

Rust编程循环

2009-12-29 10:22:51

Scala 2.8

2018-09-27 15:25:08

正则表达式前端

2011-05-30 16:11:46

Javascript

2009-07-21 14:30:38

Scalatry-catch

2014-01-05 17:41:09

PostgreSQL表达式

2011-03-14 14:02:55

Python

2009-07-09 09:51:07

Lambda表达式C#

2010-09-10 15:20:11

SQL函数计算表达式

2022-01-14 07:56:39

C#动态查询

2020-11-04 09:23:57

Python

2010-11-16 14:53:02

Oracle游标表达式

2021-03-24 13:17:41

编程循环语句Java

2009-09-17 09:09:50

Lambda表达式Linq查询

2020-12-18 09:05:13

算法单调栈

2010-04-28 18:17:16

Oracle CASE

2009-04-22 15:06:16

正则表达式PHP特殊字符
点赞
收藏

51CTO技术栈公众号