7.2 while循环
Scala的while循环跟其他语言用起来没多大差别。它包含了一个条件检查和一个循环体,只要条件检查为真,循环体就会一遍接着一遍地执行。来看示例7.2:
示例7.2 用while循环计算最大公约数
Scala也有do-while循环,它跟while循环类似,只不过它是在循环体之后执行条件检查而不是在循环体之前。示例7.3给出了一段用do-while来复述从标准输入读取的文本行,直到读到空行为止的Scala脚本:
示例7.3 用do-while读取标准输入
while和do-while这样的语法结构,我们称为“循环”而不是表达式,因为它们并不会返回一个有意义的值。返回值的类型是Unit。实际上存在这样一个(也是唯一的一个)类型为Unit的值,这个值叫作单元值(unit value),写作()。存在这样一个()值,是Scala的Unit跟Java的void的不同。可以尝试在解释器中键入:
由于表达式println("hi")的类型是Unit,greet被定义为一个结果类型为Unit的过程。[1]这样一来,greet返回单元值()。这一点在接下来的一行中得到了印证:变量whatAmI为Unit类型,捕获到greet的单元值返回结果,也就是()。
另一个相关的返回单元值的语法结构是对var的赋值。例如,当你尝试在Scala中像Java(或C/C++)的while循环惯用法那样使用while循环时,会遇到问题:
在编译这段代码时,Scala编译器会给出一个警告:用!=对类型为Unit的值和String做比较将永远返回true。在Java中,赋值语句的结果是被赋上的值(在本例中就是从标准输入读取的一行文本),而在Scala中赋值语句的结果永远是单元值()。因此,赋值语句“line = readLine()”将永远返回(),而不是""。这样一来,while循环的条件检查永远都不会为false,循环将无法终止。
由于while循环没有返回值,纯函数式编程语言通常都不支持。这些语言有表达式,而不是循环。尽管如此,Scala还是包括了while循环,因为有时候指令式的解决方案更易读,尤其是对于那些以指令式编程风格为主的程序员而言。举例来说,如果你想要编一段重复某个处理逻辑直到某个条件发生变化这样的算法时,while循环能够直接表达出来,而函数式的替代方案(可能用到了递归)对于某些读者而言就没那么直观了。
例如,示例7.4给出了一个计算两个数的最大公约数的另一种实现方式。[2]给x和y同样的两个值,示例7.4的gcd函数将返回跟示例7.2中的gcdLoop函数相同的结果。这两种方案的区别在于gcdLoop是指令式风格的,用到了var和while循环,而gcd是更加函数式风格的,用到了递归(gcd调用了自己),并且不需要var。
示例7.4 用递归计算最大公约数
一般来说,我们建议你像挑战var那样挑战代码中的while循环。[3]事实上,while循环和var通常都是一起出现的。由于while循环没有返回值,要想对程序产生任何效果,while循环通常要么更新一个var要么执行I/O。先前的gcdLoop示例已经很好地展示了这一点。在这个while循环执行过程中,它更新了var变量a和b。因此,我们建议你对代码中的while循环保持警惕。如果对于某个特定的while或do-while循环,找不到合理的理由来使用它,那么应该尝试采用其他方案来完成同样的工作。