鍍金池/ 教程/ Scala/ 減低代碼重復(fù)
包對(duì)象
Ordered Trait
組合和繼承–定義 final 成員
基本數(shù)據(jù)類型
Match 表達(dá)式
類和對(duì)象 (三)
操作基本數(shù)據(jù)類型
for 表達(dá)式
組合和繼承–重載成員函數(shù)和方法
類和對(duì)象 (二)
組合和繼承–定義 factory 對(duì)象
組合和繼承–多態(tài)和動(dòng)態(tài)綁定
Trait 的基本概念
if 表達(dá)式
組合和繼承–抽象類
函數(shù)–函數(shù)字面量的一些簡(jiǎn)化寫法
while 循環(huán)
組合和繼承–使用組合還是繼承?
訪問控制修飾符
Trait 示例–Rectangular 對(duì)象
組合和繼承–定義參數(shù)化成員變量
組合和繼承–定義無參數(shù)方法
類和對(duì)象 (一)
函數(shù)–閉包
函數(shù)–類成員函數(shù)
Scala 基本數(shù)據(jù)類型的實(shí)現(xiàn)方法
try 表達(dá)式處理異常
選擇瘦接口還是胖接口設(shè)計(jì)?
組合和繼承–小結(jié)
創(chuàng)建新的控制結(jié)構(gòu)
使用 import
為訪問控制修飾符添加作用域
Scala 的類層次關(guān)系
類和對(duì)象 (五)
傳名參數(shù)
柯里化函數(shù)
函數(shù)–頭等公民
組合和組合和繼承–定義 heighten 和 widen 函數(shù)
使用 Package–將代碼放入包中
隱含的 import
所有類的公共子類–底層類型
進(jìn)一步 Scala
函數(shù)–局部函數(shù)
引用包中的代碼
組合和繼承–使用 override 修飾符
組合和繼承–實(shí)現(xiàn)類 Element 的 above,beside 和 toString()方法
類和對(duì)象 (四)
函數(shù)–尾遞歸
沒有“break”和“continue”的日子
組合和繼承–調(diào)用基類構(gòu)造函數(shù)
減低代碼重復(fù)
函數(shù)–函數(shù)–可變參數(shù),命名參數(shù),缺省參數(shù)
起步 Scala
組合和繼承–擴(kuò)展類
函數(shù)–部分應(yīng)用的函數(shù)
開始神奇的 Scala編程之旅
組合和繼承–概述
Trait 用來實(shí)現(xiàn)可疊加的修改操作

減低代碼重復(fù)

在前面的文章中,我們說過 Scala 沒有內(nèi)置很多控制結(jié)構(gòu),這是因?yàn)?Scala 賦予了程序員自己擴(kuò)展控制結(jié)構(gòu)的能力。Scala 支持函數(shù)值(值的類型為函數(shù),而非函數(shù)的返回值),為避免混淆,我們使用函數(shù)類型值來指代類型為函數(shù)的值。

所有的函數(shù)可以分成兩個(gè)部分:一是共有部分,這部分在該函數(shù)的調(diào)用都是相同的,另外一部分為分公共部分,這部分在每次調(diào)用該函數(shù)上是可以不同的。公共部分為函數(shù)的定義體,非公共部分為函數(shù)的參數(shù)。但你使用函數(shù)類型值做為另外一個(gè)函數(shù)的參數(shù)時(shí),函數(shù)的非公共部分本身也是一個(gè)算法(函數(shù)),調(diào)用該函數(shù)時(shí),每次你都可以傳入不同函數(shù)類型值作為參數(shù),這個(gè)函數(shù)稱為高階函數(shù)–函數(shù)的參數(shù)也可以是另外一個(gè)函數(shù)。

使用高級(jí)函數(shù)可以幫助你簡(jiǎn)化代碼,它支持創(chuàng)建一個(gè)新的程序控制結(jié)構(gòu)來減低代碼重復(fù)。比如,你打算寫一個(gè)文件瀏覽器,你需要寫一個(gè) API 支持搜索給定條件的文件。首先,你添加一個(gè)方法,該方法可以通過查詢包含給定字符串的文件,比如你可以查所有“.scala”結(jié)尾的文件。你可以定義如下的 API:

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles
  def filesEnding(query : String) =
    for (file <-filesHere; if file.getName.endsWith(query))
      yield file
}

filesEnding 方法從本地目錄獲取所有文件(方法 filesHere),然后使用過濾條件(文件以給定字符串結(jié)尾)輸出給定條件的文件。

到目前為止,這代碼實(shí)現(xiàn)非常好也沒有什么重復(fù)的代碼。后來,你有需要使用新的過濾條件,文件名包含指定字符串,而不僅僅以某個(gè)字符串結(jié)尾的文件列表。你有實(shí)現(xiàn)了下面的 API。

def filesContaining( query:String ) =
    for (file <-filesHere; if file.getName.contains(query))
      yield file

filesContaining 和 filesEnding 的實(shí)現(xiàn)非常類似,不同點(diǎn)在于一個(gè)使用 endsWith,另一個(gè)使用 contains 函數(shù)調(diào)用。有過了一段時(shí)間,你有想支持使用正則表達(dá)式來查詢文件,你有實(shí)現(xiàn)了下面的對(duì)象方法:

def filesRegex( query:String) =
   for (file <-filesHere; if file.getName.matches(query))
      yield file

這三個(gè)函數(shù)的算法非常類似,所不同的是過濾條件稍有不同,在 Scala 中我們可以定義一個(gè)高階函數(shù),將這三個(gè)不同過濾條件抽象稱一個(gè)函數(shù)作為參數(shù)傳給搜索算法,我們可以定義這個(gè)高階函數(shù)如下:

def filesMatching( query:String, 
    matcher: (String,String) => Boolean) = {
    for(file <- filesHere; if matcher(file.getName,query))
      yield file
   }

這個(gè)函數(shù)的第二個(gè)參數(shù) matcher 的類型也為函數(shù)(如果你熟悉 C#,類似于 delegate),該函數(shù)的類型為 (String,String ) =>Boolean,可以匹配任意使用兩個(gè) String 類型參數(shù),返回值類型為 Boolean 的函數(shù)。使用這個(gè)輔助函數(shù),我們可以重新定義 filesEnding,filesContaining 和 filesRegex。

def filesEnding(query:String) =
   filesMatching(query,_.endsWith(_))
def filesContaining(query:String)=
   filesMatching(query,_.contains(_))
def filesRegex(query:String) =
   filesMatching(query,_.matches(_))

這個(gè)新的實(shí)現(xiàn)和之前的實(shí)現(xiàn)已經(jīng)簡(jiǎn)化了不少,實(shí)際上代碼還可以簡(jiǎn)化,我們注意到參數(shù) query 在 filesMatching 的作用只是把它傳遞給 matcher 參數(shù),這種參數(shù)傳遞實(shí)際也是無需的,簡(jiǎn)化后代碼如下:

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles
  def filesMatching(
    matcher: (String) => Boolean) = {
    for(file <- filesHere; if matcher(file.getName))
      yield file
   }
  def filesEnding(query:String) =
   filesMatching(_.endsWith(query))
def filesContaining(query:String)= 
   filesMatching(_.contains(query))
def filesRegex(query:String) = 
   filesMatching(_.matches(query))
}

函數(shù)類型參數(shù) .endsWith(query),.contains(query)和_.matches(query)為函數(shù)閉包,因?yàn)樗鼈兘壎艘粋€(gè)自由變量 query,因此我們可以看到閉包也可以用來簡(jiǎn)化代碼。