第6步 用foreach和for遍历
你可能还没有意识到,当你在前一步写下while循环时,实际上是在以指令式(imperative)的风格编程。指令式编程风格也是类似Java、C++、C这样的语言通常的风格,依次给出执行指令,通过循环来遍历,而且还经常变更被不同函数共享的状态。Scala允许你以指令式的风格编程,不过随着你对Scala的了解日益加深,你应该经常会发现自己倾向于使用更加函数式(functional)的风格。事实上,本书一个主要的目标就是帮助你像适应指令式编程风格那样,也能习惯和适应函数式编程风格。
函数式编程语言的主要特征之一就是函数是一等的语法单元,Scala非常符合这个描述。举例来说,打印每一个命令行参数的另一种(精简得多的)方式是:
在这段代码中,对args执行foreach方法,传入一个函数。在本例中,你传入的是一个函数字面量(function literal),这个(匿名)函数接收一个名为arg的参数。函数体为println(arg)。如果把上述内容录入到一个新的名为pa.scala的文件并执行:
应该会看到:
在前面的示例中,Scala解释器推断出arg的类型是String,因为String是调用foreach那个数组的元素类型。如果倾向于更明确的表达,也可以指出类型名。不过当你这样做的时候,需要将参数的部分包在圆括号里(这是函数字面量的常规语法):
执行这个脚本的效果跟前一个脚本一致。
假如你更喜欢精简的表达而不是事无巨细,可以利用Scala对函数字面量的一个特殊简写规则。如果函数字面量只是一个接收单个参数的语句,可以不必给出参数名和参数本身。[10]因此,下面这段代码依然是可以工作的:
我们来总结一下,函数字面量的语法是:用圆括号括起来的一组带名字的参数、一个右箭头和函数体,如图2.2所示。
图2.2 Scala的函数字面量语法
至此,你也许会好奇,指令式编程语言(比如Java或C)中我们熟知的那些for循环到哪里去了。为了鼓励和引导大家使用更函数式的编程风格,Scala只支持指令式for语句的函数式亲戚(这个亲戚叫作for表达式)。在读到第7.3节之前,你可能无法领略for表达式的全部功能和超强的表达力,我们将带你快速体验一把。在一个新的名为forargs.scala文件中录入以下内容:
在“for”之后的括号里包含了一句arg <- args。[11]位于<-符号右边的,是我们熟知的args数组。而在<-符号的左边是“arg”,这是一个val变量的名字,注意它不是var(因为它总是val,只能写成“arg”而不是“val arg”)。尽管arg看上去像是var,因为每一次迭代都会拿到新的值,但它确实是个val——arg不能在for表达式的循环体内被重新赋值。实际情况是,对于args数组中的每一个元素,一个新的名为arg的val会被创建出来,初始化成元素的值,这时for表达式的循环体才被执行。
如果用下面的命令执行forargs.scala脚本:
将会看到:
Scala的for表达式能做到的远不止这些,不过这个示例代码已经足以让你用起来了。我们将在第7.3节以及第23章更详细地介绍for表达式。