
第6步 用foreach方法和for-do遍历
你可能还没有意识到,当你在前一步写下while循环时,实际上是在以指令式(imperative)的风格编程。指令式编程风格也是类似Java、C++、Python这样的语言通常的风格,需要依次给出执行指令,通过循环来遍历,而且经常变更被不同函数共享的状态。Scala允许以指令式的风格编程,不过随着对Scala的了解日益加深,你应该经常会发现自己倾向于使用更加函数式(functional)的风格。事实上,本书的一个主要目标就是帮助你像适应指令式编程风格那样,也能习惯和适应函数式编程风格。
函数式编程语言的主要特征之一就是函数是一等的语法单元,Scala非常符合这个描述。举例来说,打印每一个命令行参数的另一种(精简得多的)方式是:

在这段代码中,对args执行foreach方法,传入一个函数。在本例中,传入的是一个函数字面量(function literal),这个(匿名)函数接收一个名称为arg的参数。函数体为println(arg)。如果你把上述内容录入一个新的名称为pa.scala的文件中并执行:

则应该会看到:

在前面的示例中,Scala编译器推断出arg的类型是String,因为String是调用foreach那个数组的元素类型。如果你倾向于更明确的表达方式,也可以指出类型名。不过当你这样做的时候,需要将参数的部分包在圆括号里(这是函数字面量的常规语法):

执行这个脚本的效果与执行前一个脚本的效果一致。
如果你更喜欢精简的表达方式而不是事无巨细的表达方式,则可以利用Scala对函数字面量的一个特殊简写规则。如果函数字面量只是一个接收单个参数的语句,则可以不必给出参数名和参数本身。[11]因此,下面这段代码依然是可以工作的:

我们来总结一下,Scala的函数字面量语法是:用圆括号括起来的一组带名称的参数、一个右箭头和函数体,如图2.2所示。

图2.2 Scala的函数字面量语法
至此,你也许会好奇,我们熟知的指令式编程语言(如Java或Python)中那些for循环到哪里去了。为了鼓励和引导大家使用更函数式的编程风格,Scala只支持指令式for语句的函数式亲戚(这个亲戚叫作for表达式)。在读到7.3节之前,你可能无法领略for表达式的全部功能和超强的表达力,在此我们将带你快速地体验一下。在一个新的名称为forargs.scala的文件中录入以下内容:

在“for”和“do”之间是“arg <- args”。[12]位于<-符号右边的,是我们熟知的args数组。而在<-符号的左边的是“arg”,这是一个val变量的名称,注意它不是var。(因为它总是val,所以只需要写“arg”而不用写成“val arg”。)虽然arg看上去像是var,因为每一次迭代都会拿到新的值,但是它确实是一个val——arg不能在for表达式的循环体内被重新赋值。实际情况是,对于args数组中的每一个元素,一个“新的”名称为arg的val会被创建出来,并初始化成元素的值,这时for表达式的循环体才被执行。
如果用下面的命令执行forargs.scala脚本:

将会看到:

Scala的for表达式能做到的远不止这些,不过这个示例代码已经足以让你用起来了。我们将在7.3节及《Scala高级编程》中更详细地介绍for表达式。