Scala:如何編寫新的控制結(jié)構(gòu)
擁有第一類函數(shù)的語言中,即使語言的語法是固定的,你也可以有效地制作新的控制結(jié)構(gòu)。所有你需要做的就是創(chuàng)建帶函數(shù)做參數(shù)的方法。例如,下面是“雙倍”控制結(jié)構(gòu),能夠重復(fù)一個操作兩次并返回結(jié)果:
- scala> def twice(op: Double => Double, x: Double) = op(op(x))
- twice: ((Double) => Double,Double)Double
- scala> twice(_ + 1, 5)
- res9: Double = 7.0
51CTO編輯推薦:Scala編程語言專題
這個例子里op的類型是Double => Double,就是說它是帶一個Double做參數(shù)并返回另一個Double的函數(shù)。
任何時候你發(fā)現(xiàn)你的代碼中多個地方有重復(fù)的控制模式,你就應(yīng)該考慮把它實現(xiàn)為一個新的控制結(jié)構(gòu)。本章早些時候你看到了filesMatching,一個極度特化了的控制模式。現(xiàn)在考慮一個更寬泛使用的代碼模式:打開一個資源,對它進行操作,然后關(guān)閉資源。你可以使用如下的方法將其捕獲并放入控制抽象:
有了這個方法,你就可以這樣使用:
- def withPrintWriter(file: File, op: PrintWriter => Unit) {
- val writer = new PrintWriter(file)
- try {
- op(writer)
- } finally {
- writer.close()
- }
- }
使用這個方法的好處是,由withPrintWriter而不是用戶的代碼,確認文件在結(jié)尾被關(guān)閉。因此忘記關(guān)閉文件是不可能的。這個技巧被稱為貸出模式:loan pattern,因為控制抽象函數(shù),如withPrintWriter,打開了資源并“貸出”給函數(shù)。例如,前面例子里的withPrintWriter把PrintWriter借給函數(shù)op。當函數(shù)完成的時候,它發(fā)出信號說明它不再需要“借”的資源。于是資源被關(guān)閉在finally塊中,以確信其確實被關(guān)閉,而忽略函數(shù)是正常結(jié)束返回還是拋出了異常。
- withPrintWriter(
- new File("date.txt"),
- writer => writer.println(new java.util.Date)
- )
讓客戶代碼看上去更像內(nèi)建控制結(jié)構(gòu)的一種方式是使用大括號代替小括號包圍參數(shù)列表。Scala的任何方法調(diào)用,如果你確實只傳入一個參數(shù),就能可選地使用大括號替代小括號包圍參數(shù)。例如,代之以:
你可以寫成:
- scala> println("Hello, world!")
- Hello, world!
在第二個例子里,你使用了大括號替代小括號包圍println的參數(shù)。然而,這個大括號技巧僅在你傳入一個參數(shù)時有效。下面是破壞這個規(guī)則的嘗試:
- scala> println { "Hello, world!" }
- Hello, world!
因為你正打算把兩個參數(shù)傳入substring,當你嘗試用大括號保衛(wèi)這些參數(shù)的時候產(chǎn)生了錯誤。為了糾正錯誤,你需要使用小括號:
- scala> val g = "Hello, world!"
- g: java.lang.String = Hello, world!
- scala> g.substring { 7, 9 }
- < console>:1: error: ';' expected but ',' found.
- g.substring { 7, 9 }
- ˆ
在傳入一個參數(shù)時可以用大括號替代小括號的機制的目的是讓客戶程序員能寫出包圍在大括號內(nèi)的函數(shù)文本。這可以讓方法調(diào)用感覺更像控制抽象。以前面例子里定義的withPrintWriter方法舉例。在它最近的形式里,withPrintWriter帶了兩個參數(shù),因此你不能使用大括號。雖然如此,因為傳遞給withPrintWriter的函數(shù)是列表的最后一個參數(shù),你可以使用curry化把第一個參數(shù),F(xiàn)ile拖入分離的參數(shù)列表。這將使函數(shù)僅剩下列表的第二個參數(shù)作為唯一的參數(shù)。代碼9.4展示了你要怎樣重新定義withPrintWriter。
- scala> g.substring(7, 9)
- res12: java.lang.String = wo
代碼 9.4 使用貸出模式寫文件
- def withPrintWriter(file: File)(op: PrintWriter => Unit) {
- val writer = new PrintWriter(file)
- try {
- op(writer)
- } finally {
- writer.close()
- }
- }
新的版本不同于舊版本的地方僅在于現(xiàn)在它有兩個參數(shù)列表每個里面有一個參數(shù)替代了原來的一個參數(shù)列表里面有兩個參數(shù)。僅比較這兩個參數(shù)的差異。展示在第130頁的withPrintWriter的前一個版本里,你看到了...File, op...。但在這個版本里,你看到了...File)(op...。有了上述的定義,你就可以用更賞心悅目的語法格式調(diào)用這個方法:
- val file = new File("date.txt")
- withPrintWriter(file) {
- writer => writer.println(new java.util.Date)
- }
這個例子里,第一個參數(shù)列表,包含了一個File參數(shù),被寫成包圍在小括號中。第二個參數(shù)列表,包含了一個函數(shù)參數(shù),被包圍在大括號中。
【相關(guān)閱讀】