精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

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

對于學習 Scala 的 Java 開發(fā)人員來說,對象是一個比較自然、簡單的入口點。在本系列前幾期文章中,我介紹了 Scala 中一些面向?qū)ο蟮木幊谭椒ǎ@些方法實際上與 Java 編程的區(qū)別不是很大。我還向您展示了 Scala 如何重新應(yīng)用傳統(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,這會返回什么呢?從建模角度來看,它應(yīng)該返回一個 Int,表示程序員的年薪;但是這里有一個問題,如果程序員不在數(shù)據(jù)庫中(可能根本沒有雇用她,或者已經(jīng)被解雇,要不就是輸錯了名字……),那么應(yīng)該返回什么。如果返回類型是 Int,則不能返回 null,這個 “標志” 通常表示沒有在數(shù)據(jù)庫中找到該用戶(您可能認為應(yīng)該拋出一個異常,但是大多數(shù)時候數(shù)據(jù)庫丟失值并不能視為異常,因此不應(yīng)該在這里拋出異常)。

在 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 的返回值實際上并不對應(yīng)于傳遞的鍵。相反,它是一個 Option[T] 實例,可以是與某個值有關(guān)的 Some(),也可以是 None,因此可以很清晰地表示沒有在 map 中找到鍵。如果它可以表示 map 上存在某個鍵,但是有對應(yīng)的 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 程序員解決問題。一些模式愛好者建議每種類型都應(yīng)該有一個對應(yīng)的 “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。

表面上看,這似乎很合理,但是事情很快就變得復(fù)雜起來。int 和 int? 是否應(yīng)該被視為可兼容類型,如果是的話,什么時候?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è)計就有點復(fù)雜了:或是創(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.     // 應(yīng)該只有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ù)組時,應(yīng)該充分地利用 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 的修復(fù)方法是:以冒號結(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)在哪里?實際上,對于比較簡單的代碼,模式匹配的價值不很明顯。但是對于稍微復(fù)雜的代碼,例如擴展示例以匹配特定值,那么模式匹配非常有幫助。

清單 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)  

示例很快會變得非常復(fù)雜,特別是正則表達式或 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

ScalaSet類Map類

2009-07-09 00:25:00

ScalaListTuple

2009-08-20 09:56:05

點贊
收藏

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

成人av电影在线网| 欧美88av| 制服丝袜中文字幕亚洲| 影音先锋男人的网站| 亚洲老妇色熟女老太| 国产精品亚洲欧美| 主播福利视频一区| 四虎成人免费视频| 亚洲第一会所| 亚洲乱码一区二区三区在线观看| 韩日午夜在线资源一区二区| 波多野结衣二区三区| 欧美伊人久久| 国产亚洲a∨片在线观看| 五月六月丁香婷婷| 中文字幕一区久| 亚洲精品一二三| 欧美日韩在线精品一区二区三区| 国产精品系列视频| 久久久久久穴| 欧美激情免费在线| 久久中文字幕精品| 精品视频在线你懂得| 欧美年轻男男videosbes| 亚欧无线一线二线三线区别| 男人天堂久久久| 91视频免费播放| 91香蕉亚洲精品| 中文人妻av久久人妻18| 合欧美一区二区三区| 中文字幕亚洲欧美日韩2019| av网页在线观看| 久久综合偷偷噜噜噜色| 欧美亚洲禁片免费| 国产在线青青草| 欧美videossex| 中文字幕一区在线| 日韩一区二区三区资源| 四虎精品成人免费网站| 成人免费毛片app| 91久久精品国产91久久性色tv| 精品国产青草久久久久96| 99精品热6080yy久久| 久久97久久97精品免视看 | 性色av一区二区三区四区| 亚洲福利电影| 欧美激情18p| 福利所第一导航| 亚洲国产精品久久久天堂 | 欧美老年两性高潮| 亚洲黄色a v| 成人一区福利| 欧美性猛交xxxx黑人猛交| 草b视频在线观看| 制服丝袜中文字幕在线| 亚洲精品日日夜夜| 久久免费一级片| 97caopron在线视频| 日韩美女久久久| 欧美h视频在线观看| 日日夜夜精品一区| 亚洲三级在线看| 玖玖精品在线视频| 日本在线视频www鲁啊鲁| 亚洲免费观看高清完整版在线 | 在线视频你懂得一区| 1024精品合集| 国产精品一 二 三| 无码精品黑人一区二区三区| 99免费精品在线| 精品欧美国产| 黄网站在线观看| 国产视频一区二区在线| 日韩亚洲一区在线播放| 久久99精品久久| 一区二区三区免费看视频| 性一交一乱一伧国产女士spa| 国产精品一区hongkong| 精品国产电影一区| 午夜视频在线瓜伦| 91成人小视频| 精品国产自在久精品国产| 日韩少妇一区二区| 亚洲人亚洲人色久| 中文字幕久热精品在线视频| 国产精品久久国产精麻豆96堂| 亚洲成av人片乱码色午夜| 欧美激情一区二区三区高清视频| 日产欧产va高清| 日日摸夜夜添夜夜添亚洲女人| 国产精品国产亚洲伊人久久 | av福利在线导航| 红桃视频成人在线观看| 熟女人妇 成熟妇女系列视频| 四虎影视精品永久在线观看| 欧美成人伊人久久综合网| 黄色国产在线观看| 日韩欧美精品一区| 欧美黑人巨大xxx极品| 亚洲欧美精品一区二区三区| 久久99热狠狠色一区二区| 99re在线视频上| 日本一区高清| 亚洲免费看黄网站| 日本一极黄色片| 精品视频在线一区| 日韩成人av网址| 日本高清不卡免费| 国产亚洲精品bv在线观看| 91精品久久久久久久久久入口| 人人妻人人玩人人澡人人爽| 欧美国产精品v| 2019日韩中文字幕mv| 国产成人精品一区二区三区在线| 精品国产乱码久久久久久夜甘婷婷| 怡红院一区二区三区| 欧美精品三级| 国产精品一区专区欧美日韩| 天天操天天射天天舔| 国产精品不卡在线观看| 激情综合网婷婷| 亚洲视频三区| 一区二区三区无码高清视频| 97超碰人人干| 国产精品香蕉一区二区三区| 亚洲高清乱码| 日本精品不卡| 精品粉嫩aⅴ一区二区三区四区| 18精品爽国产三级网站| 亚洲男女自偷自拍| 国产精品入口免费| 欧美a在线看| 欧美色偷偷大香| 四虎影成人精品a片| 精品91在线| 91久久精品久久国产性色也91| 国内在线精品| 欧美日韩一区二区免费视频| 一级黄色免费视频| 欧美在线高清| 成人欧美一区二区三区在线湿哒哒| 毛片在线免费| 欧美网站在线观看| 亚洲欧美色图视频| 亚洲精品1234| 国产一区免费在线| 爱福利在线视频| 日韩欧美国产1| 久久中文免费视频| 国产资源在线一区| 熟妇熟女乱妇乱女网站| 四虎视频在线精品免费网址| 色av中文字幕一区| 中文字幕在线观看视频一区| 国产欧美一二三区| 国产精品久久久久9999小说| 精品大片一区二区| 国产精品视频一区国模私拍| 精品乱码一区二区三四区视频| 精品久久久香蕉免费精品视频| 国产女主播在线播放| 激情久久五月| 国产一区二区三区黄| 黄毛片在线观看| 亚洲国产福利在线| 中文字幕亚洲高清| 久久综合久久综合久久| 成人在线观看黄| 国产一区二区三区日韩精品 | 国产又大又黄视频| 97se亚洲国产综合在线| 国产精品沙发午睡系列| 精品一区毛片| 国产女人18毛片水18精品| 日本高清在线观看wwwww色| 欧美精品自拍偷拍| 亚洲国产成人精品综合99| 成人午夜激情在线| 国产超级av在线| 日韩av久操| 亚洲影院污污.| а√在线天堂官网| 国产亚洲精品一区二区| 在线免费观看视频网站| 亚洲理论在线观看| 国产美女喷水视频| 蜜桃在线一区二区三区| 日本五级黄色片| 九九久久精品| 成人国产精品色哟哟| 不卡av免费观看| 亚洲欧洲午夜一线一品| 国产精品色综合| 亚洲福中文字幕伊人影院| 中文精品在线观看| 久久99精品一区二区三区三区| 999久久欧美人妻一区二区| 日韩深夜福利| 91九色视频导航| 51精品视频| 北条麻妃一区二区三区中文字幕| 亚洲成熟女性毛茸茸| 色综合天天综合网天天狠天天| 国产中文av在线| www.亚洲色图.com| 午夜精品久久久久久久99热影院| 影音先锋久久资源网| 中文视频一区视频二区视频三区| www.久久东京| 国产精品视频导航| 91美女主播在线视频| 在线观看日韩www视频免费| 亚洲国产精品无码久久| 欧美视频你懂的| 欧美三日本三级少妇99| 亚洲免费观看高清完整版在线观看熊 | 爱情岛论坛亚洲品质自拍视频网站| 国产亚洲成精品久久| 亚洲国产精品久久久久久6q| 欧美日韩精品一区视频| 国产一区二区99| 亚洲激情一二三区| 91禁男男在线观看| 久久久精品tv| 中文字幕无码人妻少妇免费| 国产在线一区二区| 国产裸体免费无遮挡| 国产欧美综合一区二区三区| 欧美在线观看黄| 久久久久久久久久久久久久| 色婷婷精品国产一区二区三区| 国产伦理久久久久久妇女| 亚洲bt天天射| 欧美视频精品| 国产精品欧美日韩久久| 欧美1级2级| 2018国产精品视频| 成人bbav| 久久久久久av| 欧美xxx黑人xxx水蜜桃| 免费av一区二区| 成人毛片av在线| 久久精品视频va| 18免费在线视频| 国产一区二区三区直播精品电影 | 欧美黑粗硬大| 国产精品久久在线观看| 3d性欧美动漫精品xxxx软件| 青青a在线精品免费观看| 国产在线88av| 97精品久久久中文字幕免费| av第一福利在线导航| 国内外成人免费激情在线视频| 男插女视频久久久| 欧美激情xxxx| av人人综合网| 久久久久亚洲精品国产| 国产美女情趣调教h一区二区| 久久久亚洲精选| 麻豆mv在线观看| 欧美中文字幕视频| 免费亚洲电影| 国产精品美女久久| 国产一区二区三区国产精品| av一本久道久久波多野结衣| 国产精品极品国产中出| 国产精品一区二区欧美| 日韩动漫一区| 日本一区二区三区免费观看| 日韩精品永久网址| 艳母动漫在线观看| 精品成人在线| 哪个网站能看毛片| 日本欧美一区二区| 91丝袜超薄交口足| 成人av先锋影音| 一级黄色片大全| 中国av一区二区三区| tube国产麻豆| 性久久久久久久| 天天干天天色综合| 欧美一区二区三区系列电影| 黄色av网站免费在线观看| 日韩久久精品成人| 尤物在线视频| 欧美国产第二页| 都市激情亚洲综合| 91精品免费视频| 北条麻妃在线一区二区免费播放| 麻豆亚洲一区| 国产精品99久久| 老太脱裤子让老头玩xxxxx| 日韩高清欧美激情| 日韩精品xxx| 久久久久久久久久久久久久久99 | 亚洲综合欧美激情| 丁香婷婷深情五月亚洲| 日韩中文字幕有码| 一区二区三区不卡在线观看| 天堂网中文字幕| 欧美一区二区三区免费在线看 | 九九久久国产| 国产精品免费视频一区二区 | 91p九色成人| 99久久精品久久久久久ai换脸| 免费短视频成人日韩| 欧美日韩dvd| 日韩精品亚洲一区| 亚洲少妇一区二区三区| 国产精品色婷婷久久58| 日本一级淫片色费放| 欧美夫妻性生活| 免费一级在线观看| 九九热99久久久国产盗摄| 88xx成人永久免费观看| 99热在线播放| 国产精品久久久久蜜臀| 成人在线免费播放视频| 成人免费视频一区| 天天鲁一鲁摸一摸爽一爽| 色婷婷亚洲综合| 老熟妇高潮一区二区高清视频| 久久偷看各类女兵18女厕嘘嘘| 另类激情视频| 国产日韩欧美二区| 欧美一区久久| 亚洲欧美日韩三级| 国产欧美一区二区精品婷婷| 久久国产黄色片| 精品国产91洋老外米糕| 国产一二三区在线观看| 国产久一一精品| 欧美日韩一二三四| 日本在线视频www| 99免费精品视频| 黄色激情视频在线观看| 亚洲综合五月| 91精品国产91久久久久久吃药| 精品999日本久久久影院| 亚洲午夜高清视频| 日韩成人一区二区| 91视频在线网站| 日韩欧美国产一区二区| 日本免费网站在线观看| 欧美精品video| 欧美电影院免费观看| 日本xxx免费| 国产精品一区一区| 丰满少妇被猛烈进入一区二区| 欧美喷潮久久久xxxxx| 午夜视频成人| 成人xxxxx| 艳女tv在线观看国产一区| 日本中文字幕观看| 亚洲精品国产视频| www.国产精品视频| 久久免费视频在线观看| 国偷自产视频一区二区久| 国产精品333| 91亚洲精品久久久蜜桃网站| 特级西西444www大精品视频免费看| 亚洲人精选亚洲人成在线| 欧美xxx网站| 亚洲欧美国产精品桃花| 奇米影视7777精品一区二区| 一级性生活免费视频| 日韩一区二区高清| 黄页网站大全在线免费观看| 国内精品**久久毛片app| 亚洲一区免费| 免费看的黄色录像| 欧美一区二区免费| 女同一区二区免费aⅴ| 精品欧美一区二区精品久久| 久久亚洲精选| 成年人视频软件| 日韩欧美在线一区二区三区| 国产免费拔擦拔擦8x高清在线人| 蜜桃av噜噜一区二区三区| 麻豆精品在线视频| 五月天丁香激情| 日韩精品极品视频| 日韩一区二区三免费高清在线观看| 久久久成人精品一区二区三区| 成人激情文学综合网| 久久精品视频5| 久久亚洲欧美日韩精品专区| 国产伦理久久久久久妇女| 成年网站在线免费观看| 国产精品久久久一本精品| 精品国产伦一区二区三| 欧美在线性视频| 亚洲香蕉av| 人妻熟女aⅴ一区二区三区汇编| 欧美日韩国产另类不卡| 1234区中文字幕在线观看| 亚洲一二三区精品| 99精品久久只有精品| 一区二区三区www污污污网站| 久久久久久久久国产精品|