Scala 減低代碼重復(fù)

2018-09-28 18:25 更新

減低代碼重復(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ù)可以幫助你簡化代碼,它支持創(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)簡化了不少,實(shí)際上代碼還可以簡化,我們注意到參數(shù) query 在 filesMatching 的作用只是把它傳遞給 matcher 參數(shù),這種參數(shù)傳遞實(shí)際也是無需的,簡化后代碼如下:

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,因此我們可以看到閉包也可以用來簡化代碼。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)