4.2. 函数也是对象(Functions are Objects)
既然方法是值,值是对象,方法当然也就是对象。实际上,函数类型和函数值(注意:指函数本身作为值——译注)只不过是相应的类及其实例的语法糖衣。函数类型S=>T等价于参数化类型scala.Function1[S, T],这个类型定义在Scala标准类库中:
package scala
abstract class Function1[-S,+T] {
def apply(x: S): T
}
参数超过一个的函数也可类似地定义,一般而言,n-元函数类型:(T1,T2,…,Tn)=>T被解释为Functionn[T1,T2,…,Tn,T]。也就是说,函数就是拥有apply方法的对象。例如,匿名函数“+1”:x:int=>x+1,就是如下函数Function1的实例:
new Function1[int, int] {
def apply(x: int): int = x + 1
}
反之,当一个函数类型的值被应用于参数之上(也就是调用——译注)时,这个类型的apply方法被自动插入,例如:对于Function1[S, T]类型的函数p,p(x)调用自然扩展为p.apply(x)。
4.3. 细化函数(Refining Functions)
既然Scala中函数类型是类,那么也可以再细化成为子类。以Array为例,这是一种以整数为定义域的特殊函数。Array[T]继承自Function1[int, T],并添加了数组更新、长度等方法:
package scala
class Array[T] extends Function1[int, T]
with Seq[T] {
def apply(index: int): T = ...
def update(index: int, elem: T): unit= ...
def length: int = ...
def exists(p: T => boolean): boolean = ...
def forall(p: T => boolean): boolean = ...
...
}
赋值语句左侧的函数调用是存在特殊语法的,他们使用update方法。例如,a(i)=a(i)+1被翻译成:
a.update(i, a.apply(i) + 1)
将Array存取翻译成方法调用看上去代价比较高,但是Scala中的inlining变换可以将类似于上面的代码翻译成宿主系统的原生数组存取。
上述Array类型还定义了exists和forall方法,这样也就不必手工定义了,使用这些方法,hasZeroRow可以如下定义:
def hasZeroRow(matrix: Array[Array[int]]) =
matrix exists (row => row forall (0 ==))
注意上述代码和相关操作的语言描述的对应性:“test whether in the matrix there exists a row such that in the row all elements are zeroes”(检测一个矩阵,看看它是否有一行的所有元素都等于0。这里保留英语原文,因为原文中使用斜体部分对应于上述代码的内容,体现两种语法的对应关系——译注)。还要注意一点:在上面的匿名方法中,我们略去了参数row的类型,因为其类型可以被Scala编译器根据matrix.exists方法推断出来。