国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

從Java走進Scala:使用元組、數(shù)組和列表

開發(fā) 后端
在 Scala 中,對象占有一席之地,然而,也經(jīng)常使用到一些函數(shù)類型,比如元組、數(shù)組和列表。在這一期由 Ted Neward 撰寫的流行系列文章中,您將探究 Scala 中的函數(shù)部分,并且首先研究 Scala 對函數(shù)語言中常見類型的支持。

對于學習 Scala 的 Java 開發(fā)人員來說,對象是一個比較自然、簡單的入口點。在本系列前幾期文章中,我介紹了 Scala 中一些面向?qū)ο蟮木幊谭椒ǎ@些方法實際上與 Java 編程的區(qū)別不是很大。我還向您展示了 Scala 如何重新應用傳統(tǒng)的面向?qū)ο蟾拍睿业狡淙秉c,并根據(jù) 21 世紀的新需求重新加以改造。Scala 一直隱藏的一些重要內(nèi)容將要現(xiàn)身:Scala 也是一種函數(shù)語言(這里的函數(shù)性是與其他 dys 函數(shù)語言相對而言的)。

Scala 的面向函數(shù)性非常值得探討,這不僅是因為已經(jīng)研究完了對象內(nèi)容。Scala 中的函數(shù)編程將提供一些新的設(shè)計結(jié)構(gòu)和理念以及一些內(nèi)置構(gòu)造,它們使某些場景(例如并發(fā)性)的編程變得非常簡單。

本月,您將首次進入 Scala 的函數(shù)編程領(lǐng)域,查看大多數(shù)函數(shù)語言中常見的四種類型:列表(list)、元組(tuple)、集合(set)和 Option 類型。您還將了解 Scala 的數(shù)組,后者對其他函數(shù)語言來說十分新鮮。這些類型都提出了編寫代碼的新方式。當結(jié)合傳統(tǒng)面向?qū)ο筇匦詴r,可以生成十分簡潔的結(jié)果。

使用 Option(s)

在什么情況下,“無” 并不代表 “什么也沒有”?當它為 0 的時候,與 null 有什么關(guān)系。

對于我們大多數(shù)人都非常熟悉的概念,要在軟件中表示為 “無” 是一件十分困難的事。例如,看看 C++ 社區(qū)中圍繞 NULL 和 0 進行的激烈討論,或是 SQL 社區(qū)圍繞 NULL 列值展開的爭論,便可知曉一二。 NULL 或 null 對于大多數(shù)程序員來說都表示 “無”,但是這在 Java 語言中引出了一些特殊問題。

考慮一個簡單操作,該操作可以從一些位于內(nèi)存或磁盤的數(shù)據(jù)庫查找程序員的薪資:API 允許調(diào)用者傳入一個包含程序員名字的 String,這會返回什么呢?從建模角度來看,它應該返回一個 Int,表示程序員的年薪;但是這里有一個問題,如果程序員不在數(shù)據(jù)庫中(可能根本沒有雇用她,或者已經(jīng)被解雇,要不就是輸錯了名字……),那么應該返回什么。如果返回類型是 Int,則不能返回 null,這個 “標志” 通常表示沒有在數(shù)據(jù)庫中找到該用戶(您可能認為應該拋出一個異常,但是大多數(shù)時候數(shù)據(jù)庫丟失值并不能視為異常,因此不應該在這里拋出異常)。

在 Java 代碼中,我們最終將方法標記為返回 java.lang.Integer,這迫使調(diào)用者知道方法可以返回 null。自然,我們可以依靠程序員來全面歸檔這個場景,還可以依賴程序員讀取 精心準備的文檔。這類似于:我們可以要求經(jīng)理傾聽我們反對他們要求的不可能完成的項目期限,然后經(jīng)理再進一步把我們的反對傳達給上司和用戶。

Scala 提供了一種普通的函數(shù)方法,打破了這一僵局。在某些方面,Option 類型或 Option[T],并不重視描述。它是一個具有兩個子類 Some[T] 和 None 的泛型類,用來表示 “無值” 的可能性,而不需要語言類型系統(tǒng)大費周折地支持這個概念。實際上,使用 Option[T] 類型可以使問題更加清晰(下一節(jié)將用到)。

在使用 Option[T] 時,關(guān)鍵的一點是認識到它實質(zhì)上是一個大小為 “1” 的強類型集合,使用一個不同的值 None 表示 “nothing” 值的可能性。因此,在這里方法沒有返回 null 表示沒有找到數(shù)據(jù),而是進行聲明以返回 Option[T],其中 T 是返回的原始類型。那么,對于沒有查找到數(shù)據(jù)的場景,只需返回 None,如下所示:

清單 1. 準備好踢足球了嗎?

  1. @Test def simpleOptionTest =  
  2. {  
  3.   val footballTeamsAFCEast =  
  4.     Map("New England" -> "Patriots",  
  5.         "New York" -> "Jets",  
  6.         "Buffalo" -> "Bills",  
  7.         "Miami" -> "Dolphins",  
  8.         "Los Angeles" -> null)  
  9.     
  10.   assertEquals(footballTeamsAFCEast.get("Miami"), Some("Dolphins"))  
  11.   assertEquals(footballTeamsAFCEast.get("Miami").get(), "Dolphins")  
  12.   assertEquals(footballTeamsAFCEast.get("Los Angeles"), Some(null))  
  13.   assertEquals(footballTeamsAFCEast.get("Sacramento"), None)  

注意,Scala Map 中 get 的返回值實際上并不對應于傳遞的鍵。相反,它是一個 Option[T] 實例,可以是與某個值有關(guān)的 Some(),也可以是 None,因此可以很清晰地表示沒有在 map 中找到鍵。如果它可以表示 map 上存在某個鍵,但是有對應的 null 值,這一點特別重要了。比如清單 1 中 Los Angeles 鍵。

通常,當處理 Option[T] 時,程序員將使用模式匹配,這是一個非常函數(shù)化的概念,它允許有效地 “啟用” 類型和/或值,更不用說在定義中將值綁定到變量、在 Some() 和 None 之間切換,以及提取 Some 的值(而不需要調(diào)用麻煩的 get() 方法)。清單 2 展示了 Scala 的模式匹配:

清單 2. 巧妙的模式匹配

  1. @Test def optionWithPM =  
  2. {  
  3.   val footballTeamsAFCEast =  
  4.     Map("New England" -> "Patriots",  
  5.         "New York" -> "Jets",  
  6.         "Buffalo" -> "Bills",  
  7.         "Miami" -> "Dolphins")  
  8.           
  9.   def show(value : Option[String]) =  
  10.   {  
  11.     value match  
  12.     {  
  13.       case Some(x) => x  
  14.       case None => "No team found" 
  15.     }  
  16.   }  
  17.     
  18.   assertEquals(show(footballTeamsAFCEast.get("Miami")), "Dolphins")  

C# 2.0 可變?yōu)?null 值的類型

其他語言已試圖通過各種方法解決 “可 null 值化” 問題:C++ 一直都忽略了這個問題,直至最后確定 null 和 0 是不同的值。Java 語言仍然沒有徹底解決這個問題,而是依賴于自動裝箱(autobox)— 將原語類型自動轉(zhuǎn)換為它們的包裝器對象(在 1.1 以后引入)— 幫助 Java 程序員解決問題。一些模式愛好者建議每種類型都應該有一個對應的 “Null Object”,即將自己的所有方法重寫為不執(zhí)行任何操作的類型(實際上是子類型)的實例 — 實踐證明這需要大量工作。C# 1.0 發(fā)布后,C# 設(shè)計者決定采取一種完全不同的方法解決 null 值化問題。

C# 2.0 引入了可變?yōu)?null 值的類型 的概念,重要的是添加了語法支持,認為任何特定值類型(基本指原語類型)都可以通過將 null 封裝到一個泛型/模板類 Nullable< T>,從而提供 null 支持。Nullable< T> 本身是在類型聲明中通過 ? 修飾符號引入。因此,int? 表示一個整數(shù)也可能為 null。

表面上看,這似乎很合理,但是事情很快就變得復雜起來。int 和 int? 是否應該被視為可兼容類型,如果是的話,什么時候?qū)?int 提升為 int?,反之呢?當將 int 添加到 int? 會發(fā)生什么,結(jié)果會是 null 嗎?這類問題等等。隨后類型系統(tǒng)進行了一些重要的調(diào)整,可變?yōu)?null 值的類型隨后包含到了 2.0 中 — 而 C# 程序員幾乎完全忽略了它們。

回顧一下 Option 類型的函數(shù)方法,它使 Option[T] 和 Int 之間的界限變得很清晰,看上去要比其他方法更加簡單。在那些圍繞可變?yōu)?null 值類型的反直覺(counterintuitive)提升規(guī)則之間進行比較時,尤其如此。(函數(shù)領(lǐng)域?qū)υ搯栴}近二十年的思考是值得的)。要使用 Option[T] 必須付出一些努力,但是總的來說,它產(chǎn)生了更清晰的代碼和期望。

#p#

元組和集合

在 C++ 中,我們將之稱為結(jié)構(gòu)體。在 Java 編程中,我們稱之為數(shù)據(jù)傳輸對象或參數(shù)對象。在 Scala 中,我們稱為元組。實質(zhì)上,它們是一些將其他數(shù)據(jù)類型收集到單個實例的類,并且不使用封裝或抽象 — 實際上,不 使用任何抽象常常更有用。

在 Scala 創(chuàng)建一個元組類型非常的簡單,這只是主體的一部分:如果首先將元素公開給外部,那么在類型內(nèi)部創(chuàng)建描述這些元素的名稱就毫無價值。考慮清單 3:

清單 3. tuples.scala

  1. // JUnit test suite  
  2. //  
  3. class TupleTest  
  4. {  
  5.   import org.junit._, Assert._  
  6.   import java.util.Date  
  7.    
  8.   @Test def simpleTuples() =  
  9.   {  
  10.     val tedsStartingDateWithScala = Date.parse("3/7/2006")  
  11.  
  12.     val tuple = ("Ted""Scala", tedsStartingDateWithScala)  
  13.       
  14.     assertEquals(tuple._1, "Ted")  
  15.     assertEquals(tuple._2, "Scala")  
  16.     assertEquals(tuple._3, tedsStartingDateWithScala)  
  17.   }  
  18. }  

創(chuàng)建元組非常簡單,將值放入一組圓括號內(nèi),就好象調(diào)用一個方法調(diào)用一樣。提取這些值只需要調(diào)用 “_n” 方法,其中 n 表示相關(guān)的元組元素的位置參數(shù):_1 表示第一位,_2 表示第二位,依此類推。傳統(tǒng)的 Java java.util.Map 實質(zhì)上是一個分兩部分的元組集合。

元組可以輕松地實現(xiàn)使用單個實體移動多個值,這意味著元組可以提供在 Java 編程中非常重量級的操作:多個返回值。例如,某個方法可以計算 String 中字符的數(shù)量,并返回該 String 中出現(xiàn)次數(shù)最多的字符,但是如果程序員希望同時 返回最常出現(xiàn)的字符和 它出現(xiàn)的次數(shù),那么程序設(shè)計就有點復雜了:或是創(chuàng)建一個包含字符及其出現(xiàn)次數(shù)的顯式類,或?qū)⒅底鳛樽侄伪4娴綄ο笾胁⒃谛枰獣r返回字段值。無論使用哪種方法,與使用 Scala 相比,都需要編寫大量代碼;通過簡單地返回包含字符及其出現(xiàn)次數(shù)的元組,Scala 不僅可以輕松地使用 “_1”、“_2” 等訪問元組的各個值,還可以輕松地返回多個返回值。

如下節(jié)所示,Scala 頻繁地將 Option 和元組保存到集合(例如 Array[T] 或列表)中,從而通過一個比較簡單的結(jié)構(gòu)提供了極大的靈活性和威力。

數(shù)組帶您走出陰霾

讓我們重新審視一個老朋友 — 數(shù)組 — 在 Scala 中是 Array[T]。和 Java 代碼中的數(shù)組一樣,Scala 的 Array[T] 是一組有序的元素序列,使用表示數(shù)組位置的數(shù)值進行索引,并且該值不可以超過數(shù)組的總大小,如清單 4 所示:

清單 4. array.scala

  1. object ArrayExample1  
  2. {  
  3.   def main(args : Array[String]) : Unit =  
  4.   {  
  5.     for (i <- 0 to args.length-1)  
  6.     {  
  7.       System.out.println(args(i))  
  8.     }  
  9.   }  

盡管等同于 Java 代碼中的數(shù)組(畢竟后者是最終的編譯結(jié)果),Scala 中的數(shù)組使用了截然不同的定義。對于新手,Scala 中的數(shù)組實際上就是泛型類,沒有增加 “內(nèi)置” 狀態(tài)(至少,不會比 Scala 庫附帶的其他類多)。例如,在 Scala 中,數(shù)組一般定義為 Array[T] 的實例,這個類定義了一些額外的有趣方法,包括常見的 “l(fā)ength” 方法,它將返回數(shù)組的長度。因此,在 Scala 中,可以按照傳統(tǒng)意義使用 Array,例如使用 Int 在 0 到 args.length - 1 間進行迭代,并獲取數(shù)組的第 i 個元素(使用圓括號而不是方括號來指定返回哪個元素,這是另一種名稱比較有趣的方法)。 
 
擴展數(shù)組

事實證明 Array 擁有大量方法,這些方法繼承自一個非常龐大的 parent 層次結(jié)構(gòu):Array 擴展 Array0,后者擴展 ArrayLike[A],ArrayLike[A] 擴展 Mutable[A],Mutable[A] 又擴展 RandomAccessSeq[A],RandomAccessSeq[A] 擴展了 Seq[A],等等。實際上,這種層次結(jié)構(gòu)意味著 Array 可以執(zhí)行很多操作,因此與 Java 編程相比,在 Scala 中可以更輕松地使用數(shù)組。

例如,如清單 4 所示,使用 foreach 方法遍歷數(shù)組更加簡單并且更貼近函數(shù)的方式,這些都繼承自 Iterable 特性:

清單 5. ArrayExample2

  1. object   
  2. {  
  3.   def main(args : Array[String]) : Unit =  
  4.   {  
  5.     args.foreach( (arg) => System.out.println(arg) )  
  6.   }  

看上去您沒有節(jié)省多少工作,但是,將一個函數(shù)(匿名或其他)傳入到另一個類中以便獲得在特定語義下(在本例中指遍歷數(shù)組)執(zhí)行的能力,是函數(shù)編程的常見主題。以這種方式使用更高階函數(shù)并不局限于迭代;事實上,還得經(jīng)常對數(shù)組內(nèi)容執(zhí)行一些過濾 操作去掉無用的內(nèi)容,然后再處理結(jié)果。例如,在 Scala 中,可以輕松地使用 filter 方法進行過濾,然后獲取結(jié)果列表并使用 map 和另一個函數(shù)(類型為 (T) => U,其中 T 和 U 都是泛型類型),或 foreach 來處理每個元素。我在清單 6 中采取了后一種方法(注意 filter 使用了一個 (T) : Boolean 方法,意味著使用數(shù)組持有的任意類型的參數(shù),并返回一個 Boolean)。

清單 6. 查找所有 Scala 程序員

  1. class ArrayTest  
  2. {  
  3.   import org.junit._, Assert._  
  4.     
  5.   @Test def testFilter =  
  6.   {  
  7.     val programmers = Array(  
  8.         new Person("Ted""Neward"3750000,  
  9.           Array("C++""Java""Scala""Groovy""C#""F#""Ruby")),  
  10.         new Person("Amanda""Laucher"2745000,  
  11.           Array("C#""F#""Java""Scala")),  
  12.         new Person("Luke""Hoban"3245000,  
  13.           Array("C#""Visual Basic""F#")),  
  14.   new Person("Scott""Davis"4050000,  
  15.     Array("Java""Groovy"))  
  16.       )  
  17.  
  18.     // 查找所有Scala程序員 ...  
  19.     val scalaProgs =  
  20.       programmers.filter((p) => p.skills.contains("Scala") )  
  21.       
  22.     // 應該只有2  
  23.     assertEquals(2, scalaProgs.length)  
  24.       
  25.     // ... now perform an operation on each programmer in the resulting  
  26.     // array of Scala programmers (give them a raise, of course!)  
  27.     //  
  28.     scalaProgs.foreach((p) => p.salary += 5000)  
  29.       
  30.     // Should each be increased by 5000 ...  
  31.     assertEquals(programmers(0).salary, 50000 + 5000)  
  32.     assertEquals(programmers(1).salary, 45000 + 5000)  
  33.       
  34.     // ... except for our programmers who don't know Scala  
  35.     assertEquals(programmers(2).salary, 45000)  
  36.  assertEquals(programmers(3).salary, 50000)  
  37.   }  
  38. }  

創(chuàng)建一個新的 Array 時將用到 map 函數(shù),保持原始的數(shù)組內(nèi)容不變,實際上大多數(shù)函數(shù)性程序員都喜歡這種方式:

清單 7. Filter 和 map

  1. @Test def testFilterAndMap =  
  2. {  
  3.   val programmers = Array(  
  4.       new Person("Ted""Neward"3750000,  
  5.         Array("C++""Java""Scala""C#""F#""Ruby")),  
  6.       new Person("Amanda""Laucher"2745000,  
  7.         Array("C#""F#""Java""Scala")),  
  8.       new Person("Luke""Hoban"3245000,  
  9.         Array("C#""Visual Basic""F#"))  
  10. new Person("Scott""Davis"4050000,  
  11.   Array("Java""Groovy"))  
  12.     )  
  13.  
  14.   // Find all the Scala programmers ...  
  15.   val scalaProgs =  
  16.     programmers.filter((p) => p.skills.contains("Scala") )  
  17.     
  18.   // Should only be 2  
  19.   assertEquals(2, scalaProgs.length)  
  20.     
  21.   // ... now perform an operation on each programmer in the resulting  
  22.   // array of Scala programmers (give them a raise, of course!)  
  23.   //  
  24.   def raiseTheScalaProgrammer(p : Person) =  
  25.   {  
  26.     new Person(p.firstName, p.lastName, p.age,  
  27.       p.salary + 5000, p.skills)  
  28.   }  
  29.   val raisedScalaProgs =   
  30.     scalaProgs.map(raiseTheScalaProgrammer)  
  31.     
  32.   assertEquals(2, raisedScalaProgs.length)  
  33.   assertEquals(50000 + 5000, raisedScalaProgs(0).salary)  
  34.   assertEquals(45000 + 5000, raisedScalaProgs(1).salary)  
  35. }  

注意,在清單 7 中,Person 的 salary 成員可以標記為 “val”,表示不可修改,而不是像上文一樣為了修改不同程序員的薪資而標記為 “var”。

Scala 的 Array 提供了很多方法,在這里無法一一列出并演示。總的來說,在使用數(shù)組時,應該充分地利用 Array 提供的方法,而不是使用傳統(tǒng)的 for ... 模式遍歷數(shù)組并查找或執(zhí)行需要的操作。最簡單的實現(xiàn)方法通常是編寫一個函數(shù)(如果有必要的話可以使用嵌套,如清單 7 中的 testFilterAndMap 示例所示),這個函數(shù)可以執(zhí)行所需的操作,然后根據(jù)期望的結(jié)果將該函數(shù)傳遞給 Array 中的 map、filter、foreach 或其他方法之一。

#p#

函數(shù)性列表

函數(shù)編程多年來的一個核心特性就是列表,它和數(shù)組在對象領(lǐng)域中享有相同級別的 “內(nèi)置” 性。列表對于構(gòu)建函數(shù)性軟件非常關(guān)鍵,因此,您(作為一名剛起步的 Scala 程序員)必須能夠理解列表及其工作原理。即使列表從未形成新的設(shè)計,但是 Scala 代碼在其庫中廣泛使用了列表。因此學習列表是非常必要的。

在 Scala 中,列表類似于數(shù)組,因為它的核心定義是 Scala 庫中的標準類 List[T]。并且,和 Array[T] 相同,List[T] 繼承了很多基類和特性,首先使用 Seq[T] 作為直接上層基類。

基本上,列表是一些可以通過列表頭或列表尾提取的元素的集合。列表來自于 Lisp,后者是一種主要圍繞 “LISt 處理” 的語言,它通過 car 操作獲得列表的頭部,通過 cdr 操作獲得列表尾部(名稱淵源與歷史有關(guān);第一個可以解釋它的人有獎勵)。

從很多方面來講,使用列表要比使用數(shù)組簡單,原因有二,首先函數(shù)語言過去一直為列表處理提供了良好的支持(而 Scala 繼承了這些支持),其次可以很好地構(gòu)成和分解列表。例如,函數(shù)通常從列表中挑選內(nèi)容。為此,它將選取列表的第一個元素 — 列表頭部 — 來對該元素執(zhí)行處理,然后再遞歸式地將列表的其余部分傳遞給自身。這樣可以極大減少處理代碼內(nèi)部具有相同共享狀態(tài)的可能性,并且,假如每個步驟只需處理一個元素,極有可能使代碼分布到多個線程(如果處理是比較好的)。

構(gòu)成和分解列表非常簡單,如清單 8 所示:

清單 8. 使用列表

  1. class ListTest  
  2. {  
  3.   import org.junit._, Assert._  
  4.     
  5.   @Test def simpleList =  
  6.   {  
  7.     val myFirstList = List("Ted""Amanda""Luke")  
  8.       
  9.     assertEquals(myFirstList.isEmpty, false)  
  10.     assertEquals(myFirstList.head, "Ted")  
  11.     assertEquals(myFirstList.tail, List("Amanda""Luke")  
  12.     assertEquals(myFirstList.last, "Luke")  
  13.   }  
  14. }  

注意,構(gòu)建列表與構(gòu)建數(shù)組十分相似;都類似于構(gòu)建一個普通對象,不同之處是這里不需要 “new”(這是 “case 類” 的功能,我們將在未來的文章中介紹到)。請進一步注意 tail 方法調(diào)用的結(jié)果 — 結(jié)果并不是列表的最后一個元素(通過 last 提供),而是除第一個元素以外的其余列表元素。

當然,列表的強大力量部分來自于遞歸處理列表元素的能力,這表示可以從列表提取頭部,直到列表為空,然后累積結(jié)果:

清單 9. 遞歸處理 

  1. @Test def recurseList =  
  2. {  
  3.   val myVIPList = List("Ted""Amanda""Luke""Don""Martin")  
  4.     
  5.   def count(VIPs : List[String]) : Int =  
  6.   {  
  7.     if (VIPs.isEmpty)  
  8.       0 
  9.     else 
  10.       count(VIPs.tail) + 1 
  11.   }  
  12.     
  13.   assertEquals(count(myVIPList), myVIPList.length)  

注意,如果不考慮返回類型 count,Scala 編譯器或解釋器將會出現(xiàn)點麻煩 — 因為這是一個尾遞歸(tail-recursive)調(diào)用,旨在減少在大量遞歸操作中創(chuàng)建的棧幀的數(shù)量,因此需要指定它的返回類型。即使是這樣,也可以輕松地使用 List 的 “l(fā)ength” 成員獲取列表項的數(shù)量,但關(guān)鍵是如何解釋列表處理強大的功能。清單 9 中的整個方法完全是線程安全的,因為列表處理中使用的整個中間狀態(tài)保存在參數(shù)的堆棧上。因此,根據(jù)定義,它不能被多個線程訪問。函數(shù)性方法的一個優(yōu)點就是它實際上與程序功能截然不同,并且仍然創(chuàng)建共享的狀態(tài)。

列表 API

列表具有另外一些有趣的特性,例如構(gòu)建列表的替代方法,使用 :: 方法(是的,這是一種方法。只不過名稱比較有趣)。因此,不必使用 “List” 構(gòu)造函數(shù)語法構(gòu)建列表,而是將它們 “拼接” 在一起(在調(diào)用 :: 方法時),如清單 10 所示:

清單 10. 是 :: == C++ 嗎?

  1. @Test def recurseConsedList =  
  2. {  
  3.   val myVIPList = "Ted" :: "Amanda" :: "Luke" :: "Don" :: "Martin" :: Nil  
  4.     
  5.   def count(VIPs : List[String]) : Int =  
  6.   {  
  7.     if (VIPs.isEmpty)  
  8.       0 
  9.     else 
  10.       count(VIPs.tail) + 1 
  11.   }  
  12.     
  13.   assertEquals(count(myVIPList), myVIPList.length)  

在使用 :: 方法時要小心 — 它引入了一些很有趣的規(guī)則。它的語法在函數(shù)語言中非常常見,因此 Scala 的創(chuàng)建者選擇支持這種語法,但是要正確、普遍地使用這種語法,必須使用一種比較古怪的規(guī)則:任何以冒號結(jié)束的 “名稱古怪的方法” 都是右關(guān)聯(lián)(right-associative)的,這表示整個表達式從它的最右邊的 Nil 開始,它正好是一個 List。因此,可以將 :: 認定為一個全局的 :: 方法,與 String 的一個成員方法(本例中使用)相對;這又表示您可以對所有內(nèi)容構(gòu)建列表。在使用 :: 時,最右邊的元素必須是一個列表,否則將得到一個錯誤消息。

#p#

什么是右關(guān)聯(lián)?

要更好地理解 :: 方法,要記住 “冒號” 這類操作符僅僅是一些名稱比較有趣的方法。對于普通的左管理語法,左側(cè)的標記一般是我將要對其調(diào)用方法名(右側(cè)的標記)的對象。因此,通常來說,表達式 1 + 2 在編譯器看來等同于 1.+(2)。

但是對于列表而言,這些都不適合 — 系統(tǒng)中的每個類都需要對系統(tǒng)中的所有類型使用 :: 方法,而這嚴重違背了關(guān)注點分離原則。

Scala 的修復方法是:以冒號結(jié)束的任何具有奇怪名稱的方法(例如 :: 或 :::,甚至是我自己創(chuàng)建的方法,比如 foo:)都是右關(guān)聯(lián)的。因此,比方說,a :: b :: c :: Nil 轉(zhuǎn)換為 Nil.::(c.::(b.::(a))),后者正是我需要的:List 在首位,這樣每次調(diào)用 :: 都可以獲取對象參數(shù)并返回一個 List,并繼續(xù)執(zhí)行下去。

最好為其他命名約定指定右關(guān)聯(lián)屬性,但是在撰寫本文之際,Scala 已將這條規(guī)則硬編碼到該語言中。就目前來說,冒號是惟一觸發(fā)右關(guān)聯(lián)行為的字符。
 
在 Scala 中,列表的一種最強大的用法是與模式匹配結(jié)合。由于列表不僅可以匹配類型和值,它還可以同時綁定變量。例如,我可以簡化清單 10 的列表代碼,方法是使用模式匹配區(qū)別一個至少具有一個元素的列表和一個空列表:

清單 11. 結(jié)合使用模式匹配和列表

  1. @Test def recurseWithPM =  
  2. {  
  3.   val myVIPList = "Ted" :: "Amanda" :: "Luke" :: "Don" :: "Martin" :: Nil  
  4.     
  5.   def count(VIPs : List[String]) : Int =  
  6.   {  
  7.     VIPs match  
  8.     {  
  9.       case h :: t => count(t) + 1 
  10.       case Nil => 0 
  11.     }  
  12.   }  
  13.     
  14.   assertEquals(count(myVIPList), myVIPList.length)  

在第一個 case 表達式中,將提取列表頭部并綁定到變量 h,而其余部分(尾部)則綁定到 t;在本例中,沒有對 h 執(zhí)行任何操作(實際上,更好的方法是指明這個頭部永遠不會被使用,方法是使用一個通配符 _ 代替 h,這表明它是永遠不會使用到的變量的占位符)。但是 t 被遞歸地傳遞給 count,和前面的示例一樣。還要注意,Scala 中的每一個表達式將隱式返回一個值;在本例中,模式匹配表達式的結(jié)果是遞歸調(diào)用 count + 1,當達到列表結(jié)尾時,結(jié)果為 0。

考慮到相同的代碼量,使用模式匹配的價值體現(xiàn)在哪里?實際上,對于比較簡單的代碼,模式匹配的價值不很明顯。但是對于稍微復雜的代碼,例如擴展示例以匹配特定值,那么模式匹配非常有幫助。

清單 12. 模式匹配

  1. @Test def recurseWithPMAndSayHi =  
  2. {  
  3.   val myVIPList = "Ted" :: "Amanda" :: "Luke" :: "Don" :: "Martin" :: Nil  
  4.     
  5.   var foundAmanda = false 
  6.   def count(VIPs : List[String]) : Int =  
  7.   {  
  8.     VIPs match  
  9.     {  
  10.       case "Amanda" :: t =>  
  11.         System.out.println("Hey, Amanda!"); foundAmanda = true; count(t) + 1 
  12.       case h :: t =>  
  13.         count(t) + 1 
  14.       case Nil =>  
  15.         0 
  16.     }  
  17.   }  
  18.     
  19.   assertEquals(count(myVIPList), myVIPList.length)  
  20.   assertTrue(foundAmanda)  

示例很快會變得非常復雜,特別是正則表達式或 XML 節(jié)點,開始大量使用模式匹配方法。模式匹配的使用同樣不局限于列表;我們沒有理由不把它擴展到前面的數(shù)組示例中。事實上,以下是前面的 recurseWithPMAndSayHi 測試的數(shù)組示例:

清單 13. 將模式匹配擴展到數(shù)組

  1. @Test def recurseWithPMAndSayHi =  
  2. {  
  3.   val myVIPList = Array("Ted""Amanda""Luke""Don""Martin")  
  4.  
  5.   var foundAmanda = false 
  6.     
  7.   myVIPList.foreach((s) =>  
  8.     s match  
  9.     {  
  10.       case "Amanda" =>  
  11.         System.out.println("Hey, Amanda!")  
  12.         foundAmanda = true 
  13.       case _ =>  
  14.         ; // Do nothing  
  15.     }  
  16.   )  
  17.  
  18.   assertTrue(foundAmanda)  
  19. }  

如果希望進行實踐,那么嘗試構(gòu)建清單 13 的遞歸版本,但這不用在 recurseWithPMAndSayHi 范圍內(nèi)聲明一個可修改的 var。提示:需要使用多個模式匹配代碼塊(本文的 代碼下載 中包含了一個解決方案 — 但是建議您在查看之前首先自己進行嘗試)。

結(jié)束語

Scala 是豐富的集合的集合(雙關(guān)語),這源于它的函數(shù)歷史和特性集;元組提供了一種簡單的方法,可以很容易地收集松散綁定的值集合;Option[T] 可以使用簡單的方式表示和 “no” 值相對的 “some” 值;數(shù)組可以通過增強的特性訪問傳統(tǒng)的 Java 式的數(shù)組語義;而列表是函數(shù)語言的主要集合,等等。

然而,需要特別注意其中一些特性,特別是元組:學會使用元組很容易,并且會因為為了直接使用元組而忘記傳統(tǒng)的基本對象建模。如果某個特殊元組 — 例如,名稱、年齡、薪資和已知的編程語言列表 — 經(jīng)常出現(xiàn)在代碼庫中,那么將它建模為正常的類類型和對象。

Scala 的優(yōu)點是它兼具函數(shù)性和 面向?qū)ο筇匦裕虼耍梢栽谙硎?Scala 的函數(shù)類型的同時,繼續(xù)像以前一樣關(guān)注類設(shè)計。

【相關(guān)閱讀】

  1. Scala編程語言專題
  2. 面向Java開發(fā)人員的Scala指南:當繼承中的對象遇到函數(shù)
  3. 面向Java開發(fā)人員的Scala指南:使用Scala版本的Java接口
  4. 面向Java開發(fā)人員的Scala指南:Scala控制結(jié)構(gòu)內(nèi)部揭密
  5. 面向Java開發(fā)人員的Scala指南:理解Scala的類語法和語義
責任編輯:yangsai 來源: IBMDW
相關(guān)推薦

2009-09-28 11:01:39

從Java走進Scal

2009-08-21 16:17:25

ScalaTwitter API

2009-02-04 17:32:03

ibmdwJavaScala

2009-06-16 17:54:38

Scala類語法語義

2009-06-17 11:44:22

Scala控制結(jié)構(gòu)

2009-06-19 10:51:39

Scalapackage訪問修飾符

2009-12-09 09:15:47

從Java走進ScalTwitter API

2009-07-15 10:14:25

Scala并發(fā)性

2009-08-14 11:35:01

Scala Actor

2009-10-14 11:14:38

ScitterScalaTwitter

2009-06-16 17:09:17

Scala面向?qū)ο?/a>函數(shù)編程

2009-06-19 11:13:47

Scalacase類模式匹配

2009-06-17 13:26:06

scala繼承模型

2023-06-30 17:56:31

Scala元組

2009-06-19 13:16:36

Scala計算器解析器組合子

2009-06-19 11:42:09

Scala計算器解析

2021-01-13 05:18:50

數(shù)據(jù)類型性能

2009-07-09 00:25:00

ScalaListTuple

2009-07-09 00:25:00

ScalaSet類Map類

2010-07-20 13:02:08

Perl數(shù)組
點贊
收藏

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

第一福利在线视频| 色播五月综合网| 天然素人一区二区视频| 亚洲精品久久久久久国产精华液| 3d精品h动漫啪啪一区二区| 亚洲高清黄色| 午夜av一区二区| 国产精品专区在线| 国产精品丝袜xxxxxxx| 免费人成短视频在线观看网站 | 这里是久久伊人| 日韩中文字幕一区| 亚洲另类春色校园小说| 国产亚洲视频在线观看| 麻豆视频在线观看免费| 一区二区三区欧美日韩| 欧美成人免费在线观看视频| 国产欧美日本| 成人精品久久久| 精品伊人久久久| 国产一区二区三区久久精品| 最爽无遮挡行房视频在线| 精品久久久久久国产91| 最近免费中文字幕中文高清百度| 久久电影国产免费久久电影| 精品一区日韩成人| 93在线视频精品免费观看| 午夜精品福利在线观看| 久久精品国产福利| 永久免费毛片在线播放不卡| √天堂8资源中文在线| 欧美变态口味重另类| 日韩av中文| 欧美亚日韩国产aⅴ精品中极品| 一二三四社区在线视频6| 亚洲激情在线激情| eeuss鲁片一区| 亚洲国产精品成人综合色在线婷婷| 国产在线视频在线| 国产盗摄精品一区二区三区在线| 婷婷视频在线播放| 国产乱子轮精品视频| av久久久久久| 91一区二区三区在线观看| 免费成人在线视频网站| 久久嫩草精品久久久久| 一级黄色香蕉视频| 日韩美女精品在线| 在线影院福利| 在线观看亚洲一区| h视频在线免费观看| 精品久久久久一区| 亚洲播播91| 欧美成人三级视频网站| 成人爽a毛片免费啪啪红桃视频| 欧美激情精品久久久久久| 一区二区精彩视频| 欧美一级大片在线观看| 国产国产精品| 国产伦精品一区二区三区免费视频 | av在线播放亚洲| 99免费精品视频| 亚洲精品高清无码视频| 亚洲丝袜制服诱惑| 亚洲1024| 3d动漫精品啪啪一区二区竹菊| 丰满的护士2在线观看高清| 亚洲小视频在线| 99香蕉久久| 国产美女久久精品香蕉69| 中文字幕毛片| 国模精品视频一区二区| 影音先锋日韩在线| 精品国产免费人成电影在线观看四季 | 四虎精品成人影院观看地址| 亚洲视频狠狠干| 日本中文字幕网址| 蜜臀精品久久久久久蜜臀| 国产免费一区| 九九在线精品| 精品国产电影一区二区| 高清一区二区三区av| 91精品国产一区二区| 最近中文视频在线| 91麻豆产精品久久久久久| 99在线观看视频网站| 亚洲精品国产成人影院| 国产成人鲁鲁免费视频a| 日韩三级久久| 国产精品福利无圣光在线一区| 亚洲国产精品成人一区二区在线| 亚洲欧洲成人av每日更新| av在线com| 狠狠久久亚洲欧美专区| 黄网站免费观看| 在线观看国产精品网站| 国产精品xxx| 精品99在线视频| 亚洲一区中文在线| 日韩子在线观看| 一本色道久久综合狠狠躁篇怎么玩 | 日韩av免费| 久久人人爽国产| 99pao成人国产永久免费视频| 92看片淫黄大片一级| 国产日韩欧美精品一区| www.国产在线视频| 91色porny| 小明精品国产一区二区三区| 国产精品不卡一区二区三区| 日本v片在线免费观看| 欧美二区三区91| 66av99| 国产精品不卡视频| 日本韩国一区| 欧美日韩精品一区二区天天拍小说| 97视频免费| 国产精品嫩草影院com| 青草久久伊人| 亚洲国产精品va在线看黑人| 最近中文字幕免费mv2018在线| 欧美一区午夜视频在线观看| 极品在线视频| 日韩亚洲欧美成人一区| yellow91字幕网在线| 日韩欧美国产1| 欧美va在线观看| av观看久久| 国产精品无码永久免费888| 国产精品亚洲产品| 国产99re66在线视频| 超薄肉色丝袜足j调教99| 亚洲美女视频网站| 99国产成+人+综合+亚洲欧美| 99久久免费观看| 欧美日韩在线另类| 成a人片在线观看| 日韩精品一区二区三区老鸭窝| a天堂中文在线88| 欧美日韩成人在线| 欧美日韩 国产精品| 精品中文字幕av| 九九热在线精品视频| 9国产精品视频| 成人精品福利| 国内精品国产三级国产在线专| 国产成人久久精品77777最新版本 国产成人鲁色资源国产91色综 | 视频一区视频二区欧美| 高潮白浆视频| 正在播放国产精品| 久久亚洲私人国产精品va| 亚洲欧美日本在线| 99精品热6080yy久久| 2020国产精品小视频| 缅甸午夜性猛交xxxx| 久久人人爽国产| 男男gaygays亚洲| 久久久久久久久久久99999| 亚洲高清资源| www.久久热| 免费高清在线| 亚洲自拍偷拍二区| 久久6精品影院| 亚洲国产欧美日韩精品| 精品日本高清在线播放| 国产精品久久久久婷婷二区次| 91丝袜美腿高跟国产极品老师| 国精品一区二区三区| 精品久久久网| 免费成人在线电影| 午夜精品久久久久久久男人的天堂| www亚洲欧美| 中文字幕亚洲二区| 91精品免费在线| 亚洲视频每日更新| 亚洲精品成人悠悠色影视| 欧美经典一区二区| 26uuu另类欧美| 亚洲区欧美区| 日本三级亚洲精品| av电影在线观看不卡| 亚洲福利视频三区| 国产福利91精品一区| 中文在线视频| 国产一区深夜福利| 欧美午夜性色大片在线观看| 中文字幕一区二区三区欧美日韩| 东凛在线观看| 一本久久a久久精品vr综合| 日韩av在线不卡| 91麻豆福利精品推荐| 精品免费在线| 麻豆传媒视频在线观看| 欧美性受黑人性爽| 午夜精品久久久久久久白皮肤| 五月婷婷综合网| 久久激情婷婷| 国产在线不卡一区二区三区| 一级毛片免费视频| 午夜久久资源|