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方法推斷出來。