自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

減少Scala中的代碼重復(fù)

開(kāi)發(fā) 后端
本文節(jié)選自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻譯的《Programming in Scala》的第九章。Scala是一種針對(duì) JVM 將函數(shù)和面向?qū)ο蠹夹g(shù)組合在一起的編程語(yǔ)言。

所有的函數(shù)都被分割成通用部分,它們?cè)诿看魏瘮?shù)調(diào)用中都相同,以及非通用部分,在不同的函數(shù)調(diào)用中可能會(huì)變化。通用部分是函數(shù)體,而非通用部分必須由參數(shù)提供。當(dāng)你把函數(shù)值用做參數(shù)時(shí),算法的非通用部分就是它代表的某些其它算法。在這種函數(shù)的每一次調(diào)用中,你都可以把不同的函數(shù)值作為參數(shù)傳入,于是被調(diào)用函數(shù)將在每次選用參數(shù)的時(shí)候調(diào)用傳入的函數(shù)值。這種高階函數(shù):higher-order function——帶其它函數(shù)做參數(shù)的函數(shù)——給了你額外的機(jī)會(huì)去組織和簡(jiǎn)化代碼。

51CTO編輯推薦:Scala編程語(yǔ)言專題

高階函數(shù)的一個(gè)好處是它們能讓你創(chuàng)造控制抽象從而使你減少代碼重復(fù)。例如,假設(shè)你正在寫一個(gè)文件瀏覽器,并且你想要提供一個(gè)API,能夠允許使用者搜索匹配某些標(biāo)準(zhǔn)的文件。首先,你加入了搜索文件名結(jié)束于特定字串的機(jī)制。這能讓你的用戶發(fā)現(xiàn),比方說(shuō),所有擴(kuò)展名為“.scala”的文件。你可以通過(guò)在單例對(duì)象中定義公開(kāi)的filesEnding方法提供這樣的API,如:

  1. object FileMatcher {  
  2.  private def filesHere = (new java.io.File(".")).listFiles  
  3.  def filesEnding(query: String) =  
  4.   for (file < - filesHere; if file.getName.endsWith(query))  
  5.    yield file  
  6. }  
filesEnding方法通過(guò)使用私有幫助方法filesHere接受當(dāng)前目錄所有文件的列表,然后基于是否每個(gè)文件名以用戶特定的查詢結(jié)尾來(lái)過(guò)濾它們。由于filesHere是私有的,filesEnding方法是定義在你提供給你用戶的API,F(xiàn)ilesMatcher中唯一可以訪問(wèn)的方法。

目前為止還挺好,沒(méi)有重復(fù)的代碼。然而后來(lái),你決定讓別人可以基于文件名的任何部分做查詢。這個(gè)功能可以良好地用于以下情況:你的用戶記不住他們是以phb-important.doc,stupid-pub-report.doc,may2003salesdoc.phb,或什么完全不同的名字來(lái)命名文件的,但他們認(rèn)為“phb”出現(xiàn)在文件的什么地方。你回到工作并把這個(gè)函數(shù)加到你的API,F(xiàn)ileMatcher中:

  1. def filesContaining(query: String) =  
  2.  for (file < - filesHere; if file.getName.contains(query))  
  3.   yield file  
這段函數(shù)與filesEnding很像。它搜索filesHere,檢查名稱,并且如果名稱匹配則返回文件。唯一的差別是這個(gè)函數(shù)使用了contains替代endsWith。

隨著時(shí)間的推移,程序變得更加成功。最后,你屈服于幾個(gè)強(qiáng)勢(shì)用戶的需求,他們想要基于正則表達(dá)式搜索。這些馬虎的家伙擁有數(shù)千個(gè)文件的超大目錄,他們希望能做到像發(fā)現(xiàn)所有在題目中什么地方包含“oopsla”的“pdf”文件這樣的事。為了支持他們,你寫了這個(gè)函數(shù):

  1. def filesRegex(query: String) =  
  2.  for (file < - filesHere; if file.getName.matches(query))  
  3.   yield file  
有經(jīng)驗(yàn)的程序員會(huì)注意到所有的這些重復(fù)并想知道是否能從中提煉出通用的幫助函數(shù)。然而,顯而易見(jiàn)的方式不起作用。你希望能做的的是這樣的:

  1. def filesMatching(query: String, method) =  
  2.  for (file < - filesHere; if file.getName.method(query))  
  3.   yield file  
這種方式在某些動(dòng)態(tài)語(yǔ)言中能起作用,但Scala不允許在運(yùn)行期這樣粘合代碼。那么你該做什么呢?

函數(shù)值提供了一個(gè)答案。雖然你不能把方法名當(dāng)作值傳遞,但你可以通過(guò)傳遞為你調(diào)用方法的函數(shù)值達(dá)到同樣的效果。在這個(gè)例子里,你可以給方法添加一個(gè)matcher參數(shù),其唯一的目的就是針對(duì)查詢檢查文件名:

  1. def filesMatching(query: String,  
  2.   matcher: (String, String) => Boolean) = {  
  3.  for (file < - filesHere; if matcher(file.getName, query))  
  4.   yield file  
  5. }  
方法的這個(gè)版本中,if子句現(xiàn)在使用matcher針對(duì)查詢檢查文件名。更精確的說(shuō)法是這個(gè)檢查不依賴于matcher定義了什么?,F(xiàn)在看一下matcher的類型。它是一個(gè)函數(shù),因此類型中有個(gè)=>。這個(gè)函數(shù)帶兩個(gè)字串參數(shù)——文件名和查詢——并返回布爾值,因此這個(gè)函數(shù)的類型是(String, String) => Boolean。

有了這個(gè)新的filesMatching幫助方法,你可以通過(guò)讓三個(gè)搜索方法調(diào)用它,并傳入合適的函數(shù)來(lái)簡(jiǎn)化它們:

  1. def filesEnding(query: String) =  
  2.  filesMatching(query, _.endsWith(_))  
  3. def filesContaining(query: String) =  
  4.  filesMatching(query, _.contains(_))  
  5. def filesRegex(query: String) =  
  6.  filesMatching(query, _.matches(_))  
這個(gè)例子中展示的函數(shù)文本使用了前一章中介紹的占位符語(yǔ)法,對(duì)你來(lái)說(shuō)可能感覺(jué)不是非常自然。因此,以下闡明例子里是如何使用占位符的。用在filesEnding方法里的函數(shù)文本_.endsWith(_),與下面的是一回事:

  1. (fileName: String, query: String) => fileName.endsWith(query) 
原因是filesMatching帶一個(gè)函數(shù),這個(gè)函數(shù)需要兩個(gè)String參數(shù),不過(guò)你不需要指定參數(shù)類型。因此,你也可以寫成(fileName, query) => fileName.endsWith(query)。由于第一個(gè)參數(shù),fileName,在方法體中被第一個(gè)使用,第二個(gè)參數(shù),query,第二個(gè)使用,你也可以使用占位符語(yǔ)法:_.endsWith(_)。第一個(gè)下劃線是第一個(gè)參數(shù),文件名的占位符,第二個(gè)下劃線是第二個(gè)參數(shù),查詢字串的占位符。

代碼已經(jīng)被簡(jiǎn)化了,但它實(shí)際還能更短。注意到query傳遞給了filesMatching,但filesMatching沒(méi)有用查詢做任何事只是把它傳回給傳入的matcher函數(shù)。這個(gè)傳來(lái)傳去的過(guò)程不是必需的,因?yàn)檎{(diào)用者在前面就已經(jīng)知道了query的內(nèi)容。你可以同樣從filesMatching和matcher中簡(jiǎn)單地去除query參數(shù),因此簡(jiǎn)化后的代碼如展示在代碼9.1中那樣。

  1. object FileMatcher {  
  2.  private def filesHere = (new java.io.File(".")).listFiles  
  3.  private def filesMatching(matcher: String => Boolean) =  
  4.   for (file < - filesHere; if matcher(file.getName))  
  5.    yield file  
  6.  def filesEnding(query: String) =  
  7.   filesMatching(_.endsWith(query))  
  8.  def filesContaining(query: String) =  
  9.   filesMatching(_.contains(query))  
  10.  def filesRegex(query: String) =  
  11.   filesMatching(_.matches(query))  
  12. }  
代碼 9.1 使用閉包減少代碼重復(fù)

這個(gè)例子演示了函數(shù)作為第一類值幫助你減少代碼重復(fù)的方式,如果沒(méi)有它們這將變得很困難。比方說(shuō)在Java里,你可以創(chuàng)建包括帶一個(gè)String并返回Boolean的方法的接口,然后創(chuàng)建并傳遞實(shí)現(xiàn)這個(gè)接口的匿名內(nèi)部類實(shí)例給filesMatching。盡管這個(gè)方式能去除你嘗試簡(jiǎn)化掉的代碼重復(fù),但同時(shí)它增加了許多乃至更多的新代碼。因此好處就不值這個(gè)開(kāi)銷了,于是你或許就安于重復(fù)代碼的現(xiàn)狀了。

再者,這個(gè)例子還演示了閉包是如何能幫助你減少代碼重復(fù)的。前面一個(gè)例子里用到的函數(shù)文本,如_.endsWith(_)和_.contains(_),都是在運(yùn)行期實(shí)例化成函數(shù)值而不是閉包,因?yàn)樗鼈儧](méi)有捕獲任何自由變量。舉例來(lái)說(shuō)表達(dá)式_.endsWith(_)里用的兩個(gè)變量,都是用下劃線代表的,也就是說(shuō)它們都是從傳遞給函數(shù)的參數(shù)獲得的。因此,_.endsWith(_)使用了兩個(gè)綁定變量,而不是自由變量。相對(duì)的,最近的例子里面用到的函數(shù)文本_.endsWith(query),包含一個(gè)綁定變量,下劃線代表的參數(shù),和一個(gè)名為query的自由變量。僅僅因?yàn)镾cala支持閉包才使得你可以在最近的這個(gè)例子里從filesMatching中去掉query參數(shù),從而更進(jìn)一步簡(jiǎn)化了代碼。

【相關(guān)閱讀】

  1. Scala:尾遞歸的跟蹤調(diào)用及其局限
  2. Scala允許的重復(fù)參數(shù)
  3. 學(xué)習(xí)Scala的閉包
  4. Scala的偏應(yīng)用函數(shù)
  5. Scala:函數(shù)文本的短格式和占位符語(yǔ)法

責(zé)任編輯:book05 來(lái)源: Artima
相關(guān)推薦

2022-05-22 21:16:46

TypeScriptOmit 工具

2009-07-22 07:43:00

Scala重復(fù)參數(shù)

2021-04-05 22:38:30

Python操作符代碼

2021-09-03 08:21:20

前端代碼模塊

2017-03-30 10:21:47

jsinspect前端代碼庫(kù)

2009-09-09 14:09:35

Scala Trait

2009-07-22 07:47:00

Scala客戶代碼

2009-09-22 10:15:42

PersistentQScala

2009-09-22 09:59:40

QueueCollecScala

2009-07-22 07:53:00

Scala擴(kuò)展類

2009-07-08 15:35:18

Case類Scala

2009-07-22 09:29:44

ScalaSpiral程序

2023-06-12 15:33:52

Scalafor循環(huán)語(yǔ)句

2009-09-28 11:42:21

KestrelScala

2009-07-21 17:21:57

Scala定義函數(shù)

2009-09-28 11:25:17

PersistentQKestrelScala

2022-03-08 14:02:35

GuavaMapjava

2009-07-08 12:43:59

Scala ServlScala語(yǔ)言

2021-12-16 16:35:46

CSS代碼前端

2010-09-14 15:34:41

Scala
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)