scala是一種函數式編程風格的語言,除了常見的if......else ,for ,while等傳統的流程式控制制結構,也可以自定義流程式控制制的控制結構。 再瞭解scala如何實現編寫新的流程結構,我們瞭解一下頭等函數的概念; scala的函數是頭等函數(first-class function).你不
scala是一種函數式編程風格的語言,除了常見的if......else ,for ,while等傳統的流程式控制制結構,也可以自定義流程式控制制的控制結構。
再瞭解scala如何實現編寫新的流程結構,我們瞭解一下頭等函數的概念;
scala的函數是頭等函數(first-class function).你不僅可以定義和調用函數。還可以把他們寫成匿名的字面量(Iiteral),並把他們作為值(value)傳遞。
在擁有頭等函數的語言中,即使語言的語法是固定的,你也可以有效地製作新的控制流程結構,可以使用創建帶函數參數的方法來做到這點;另一種方法是
使用用花括弧代替小括弧的機制。
(一)使用創建帶函數參數的方法來編寫新的控制結構
下麵是"雙倍"控制結構的實現,能夠重覆一個操作兩次並返回結果:
def twice(op: Double => Double,x: Double) = op(op(x)) #調用方法 twice(_ + 1,5)
twice函數解析:該函數有2兩個參數,
第一個參數是function,作用是把一個double轉化成另一個double。
第二個參數double類型的數字。
op(x)的意思是調用op對象的apply() 方法,由於op是函數值對象,所以op(x)的意思是,將x應用為op函數的參數。
所以 twice(_ + 1,5) 的返回值是 7.
當你在代碼中發現,有個重覆的控制模式時,就可以考慮把這些重覆的控制模式實現成一個新的控制結構。
文件操作是的流程是:打開一個資源,對它進行操作,關閉資源。這是一個經常使用的代碼模式。我們可以新建
一個控制結構來實現這個功能。
/** * withPrintWriter 方法有兩個參數: * 第一個參數的值:file對象。 * 第一個參數的值:function類型的對象,其中function的參數是PrintWriter,返回值是Unit。 * */ def withPrintWriter(file: File,op: PrintWriter => Unit){ val writer = new PrintWriter(file) try{ op(writer) }finally{ writer.close() println("closed the print writer.") } }
//調用withPrintWriter方法
withPrintWriter(new File("D:/loader.log"), writer => {println("write a time str to file.");writer.print(new java.util.Date)})
withPrintWriter方法調用過程分析,
withPrintWriter(new File("D:/loader.log"), writer => {println("write a time str to file.");writer.print(new java.util.Date)})
第一個參數的值:file對象。
第一個參數的值:function類型的對象,該function的功能是將一個時間戳寫入到這個文件中,併在寫之前列印一條提示日誌。
使用withPrintWriter的好處是:withPrintWriter並非客戶代碼,可以確保文件在結尾被關閉;
因此客戶忘記關閉文件是不可能的。這個技巧被稱為借貸模式(loan parttern)。
拿withPrintWriter來講:withPrintWriter打開了資源並"貸出"給函數, withPrintWriter把printWriter借給
函數op;當函數op完成時,它發出信號說明它不再需要 "借"的資源PrintWriter,於是資源在finally塊中被關閉,
以確信其確實被關閉,而忽略函數是正常結束返回還是拋出了異常。
(二)使用用花括弧代替小括弧的機制實現
為了讓客戶代碼看上去更像內建控制結構的另一種方式是使用花括弧代替小括弧包圍參數列表。
scala的任何方法調用,如果你確實只傳入一個參數,就可以選擇使用花括弧代替小括弧包圍參數。
例如如下代碼:
println("hello,world") ////如果有一個參數,可以用花括弧代替小括弧包圍參數。 println{"hello,world"} val g = "Hello,world!" g.substring(7,9) // g.substring{7,9} //如果有兩個參數,就不能用花括弧代替小括弧包圍參數,會報出編譯錯誤
為了使用{}替換小括弧,使用柯里化重新定義前一函數withPrintWriter,源碼如下:
/** * 使用柯里化重新定義函數。 * scala在傳入一個參數時,可以用花括弧代替小括弧的機制: * 這個機制的目的是讓客戶端程式能寫出包圍在花括弧內的函數字面量;這可以讓方法調用感覺更像控制抽象。 */ def withPrintWriter2(file: File)(op: PrintWriter => Unit) = { val writer = new PrintWriter(file) try{ op(writer) }finally{ writer.close() } } //調用該函數。 //調用柯里化的withPrintWriter2方法,第一函數參數以小括弧包圍參數,第二個一花括弧包圍參數(參數是函數字面量)。 //這種方法調用看起來是不是比較賞心悅目啊! withPrintWriter2(new File("D:/kafka_loader.log")){ writer => writer.print(new java.util.Date) } //當然也可以都用小括弧,或者都用大括弧,也是沒有問題的?看個人喜好了,但是代碼就有點怪異! withPrintWriter2(new File("D:/kafka_loader.log"))( writer => writer.print(new java.util.Date) ) withPrintWriter2{new File("D:/kafka_loader.log")}{ writer => writer.print(new java.util.Date) } // 簡化一下調用方法 val file = new File("D:/kafka_loader.log") val func = (writer: PrintWriter) => writer.print(new java.util.Date) withPrintWriter2(file){func} //都是合法的哦,這種更像控制抽象。 withPrintWriter2(file)(func) //都是合法的哦 withPrintWriter2{file}{func} //都是合法的哦 withPrintWriter2{file}(func) //都是合法的哦