scala 集合
基本數據結構
Scala提供了一些很方便的集合類。
參考?《Effective Scala》中關于怎么使用集合類的內容。
List
| 1 2 | scala> val numbers = List(1, 2, 3, 4) numbers: List[Int] = List(1, 2, 3, 4) |
Set
集合中沒有重復元素
| 1 2 | scala> Set(1, 1, 2) res0: scala.collection.immutable.Set[Int] = Set(1, 2) |
元組(Tuple)
元組可以直接把一些具有簡單邏輯關系的一組數據組合在一起,并且不需要額外的類。
| 1 2 | scala> val hostPort = ("localhost", 80) hostPort: (String, Int) = (localhost, 80) |
和case class不同,元組的元素不能通過名稱進行訪問,不過它們可以通過基于它們位置的名稱進行訪問,這個位置是從1開始而非從0開始。
| 1 2 3 4 5 | scala> hostPort._1 res0: String = localhost scala> hostPort._2 res1: Int = 80 |
元組可以很好地和模式匹配配合使用。
| 1 2 3 4 | hostPort match { ??case ("localhost", port) => ... ??case (host, port) => ... } |
創建一個包含2個值的元組有一個很簡單的方式:->
| 1 2 | scala> 1 -> 2 res0: (Int, Int) = (1,2) |
參考?《Effective Scala》中關于解除綁定(拆封一個元組)的觀點。
Map
Map里可以存放基本的數據類型。
| 1 2 | Map(1 -> 2) Map("foo" -> "bar") |
這個看起來是一個特殊的語法,不過回想一下前面我們討論元組的時候,->符號是可以用來創建元組的。
Map()可以使用我們在第一節里講到的可變參數的語法:Map( 1 -> "one", 2 -> "two"),它會被擴展為Map((1,"one"),(2,"two")),其中第一個元素參數是key,第二個元素是value。
Map里也可以包含Map,甚至也可以把函數當作值存在Map里。
| 1 | Map(1 -> Map("foo" -> "bar")) |
| 1 | Map("timesTwo" -> { timesTwo(_) }) |
Option
Option是一個包含或者不包含某些事物的容器。
Option的基本接口類似于:
| 1 2 3 4 5 | trait Option[T] { ??def isDefined: Boolean ??def get: T ??def getOrElse(t: T): T } |
Option本身是泛型的,它有兩個子類:Some[T]和None
我們來看一個Option的示例:?Map.get使用Option來作為它的返回類型。Option的作用是告訴你這個方法可能不會返回你請求的值。
| 1 2 3 4 5 6 7 8 | scala> val numbers = Map(1 -> "one", 2 -> "two") numbers: scala.collection.immutable.Map[Int,String] = Map((1,one), (2,two)) scala> numbers.get(2) res0: Option[java.lang.String] = Some(two) scala> numbers.get(3) res1: Option[java.lang.String] = None |
現在,我們要的數據存在于這個Option里。那么我們該怎么處理它呢?
一個比較直觀的方法就是根據isDefined方法的返回結果作出不同的處理。
| 1 2 3 4 5 6 7 | //如果這個值存在的話,那么我們把它乘以2,否則返回0。 val result = if (res1.isDefined) { ??res1.get * 2 } else { ??0 } |
?
不過,我們更加建議你使用getOrElse或者模式匹配來處理這個結構。
getOrElse讓你可以很方便地定義一個默認值。
| 1 | val result = res1.getOrElse(0) * 2 |
模式匹配可以很好地和Option進行配合使用。
val result = res1 match { case Some(n) => n * 2 case None => 0 }
參考?《Effective Scala》中關于?Options的內容。
?
函數組合器
List(1,2,3) map squared會在列表的每個元素上分別應用squared函數,并且返回一個新的列表,可能是List(1,4,9)。我們把類似于map這樣的操作稱為組合器。(如果你需要一個更好的定義,你或許會喜歡Stackoverflow上的關于組合器的解釋。
map
在列表中的每個元素上計算一個函數,并且返回一個包含相同數目元素的列表。
| 1 2 | scala> numbers.map((i: Int) => i * 2) res0: List[Int] = List(2, 4, 6, 8) |
或者傳入一個部分計算的函數
| 1 2 3 4 5 | scala> def timesTwo(i: Int): Int = i * 2 timesTwo: (i: Int)Int scala> numbers.map(timesTwo _) res0: List[Int] = List(2, 4, 6, 8) |
foreach
foreach和map相似,只不過它沒有返回值,foreach只要是為了對參數進行作用。
| 1 | scala> numbers.foreach((i: Int) => i * 2) |
沒有返回值。
你可以嘗試把返回值放在一個變量里,不過它的類型應該是Unit(或者是void)
| 1 2 | scala> val doubled = numbers.foreach((i: Int) => i * 2) doubled: Unit = () |
filter
移除任何使得傳入的函數返回false的元素。返回Boolean類型的函數一般都稱為斷言函數。
| 1 2 | scala> numbers.filter((i: Int) => i % 2 == 0) res0: List[Int] = List(2, 4) |
| 1 2 3 4 5 | scala> def isEven(i: Int): Boolean = i % 2 == 0 isEven: (i: Int)Boolean scala> numbers.filter(isEven _) res2: List[Int] = List(2, 4) |
zip
zip把兩個列表的元素合成一個由元素對組成的列表里。
| 1 2 | scala> List(1, 2, 3).zip(List("a", "b", "c")) res0: List[(Int, String)] = List((1,a), (2,b), (3,c)) |
partition
partition根據斷言函數的返回值對列表進行拆分。
| 1 2 3 | scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> numbers.partition(_ %2 == 0) res0: (List[Int], List[Int]) = (List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9)) |
find
find返回集合里第一個匹配斷言函數的元素
| 1 2 | scala> numbers.find((i: Int) => i > 5) res0: Option[Int] = Some(6) |
drop & dropWhile
drop丟棄前i個元素
| 1 2 | scala> numbers.drop(5) res0: List[Int] = List(6, 7, 8, 9, 10) |
dropWhile移除前幾個匹配斷言函數的元素。例如,如果我們從numbers列表里dropWhile奇數的話,1會被移除(3則不會,因為它被2所“保護”)。
| 1 2 | scala> numbers.dropWhile(_ % 2 != 0) res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10) |
foldLeft
| 1 2 | scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n) res0: Int = 55 |
0是起始值(注意numbers是一個List[Int]),m是累加值。
更加直觀的來看:
| 1 2 3 4 5 6 7 8 9 10 11 12 | scala> numbers.foldLeft(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n } m: 0 n: 1 m: 1 n: 2 m: 3 n: 3 m: 6 n: 4 m: 10 n: 5 m: 15 n: 6 m: 21 n: 7 m: 28 n: 8 m: 36 n: 9 m: 45 n: 10 res0: Int = 55 |
foldRight
這個和foldLeft相似,只不過是方向相反。
| 1 2 3 4 5 6 7 8 9 10 11 12 | scala> numbers.foldRight(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n } m: 10 n: 0 m: 9 n: 10 m: 8 n: 19 m: 7 n: 27 m: 6 n: 34 m: 5 n: 40 m: 4 n: 45 m: 3 n: 49 m: 2 n: 52 m: 1 n: 54 res0: Int = 55 |
flatten
flatten可以把嵌套的結構展開。
| 1 2 | scala> List(List(1, 2), List(3, 4)).flatten res0: List[Int] = List(1, 2, 3, 4) |
flaoMap
flatMap是一個常用的combinator,它結合了map和flatten的功能。flatMap接收一個可以處理嵌套列表的函數,然后把返回結果連接起來。
| 1 2 3 4 5 | scala> val nestedNumbers = List(List(1, 2), List(3, 4)) nestedNumbers: List[List[Int]] = List(List(1, 2), List(3, 4)) scala> nestedNumbers.flatMap(x => x.map(_ * 2)) res0: List[Int] = List(2, 4, 6, 8) |
可以把它當作map和flatten兩者的縮寫:
| 1 2 | scala> nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).flatten res1: List[Int] = List(2, 4, 6, 8) |
這個調用map和flatten的示例是這些函數的類“組合器”特點的展示。
See Also?Effective Scala has opinions about?flatMap.
參考?《Effective Scala》中關于flatMap的內容.
廣義的函數組合器
現在,我們學習了一大堆處理集合的函數。
不過,我們更加感興趣的是怎么寫我們自己的函數組合器。
有趣的是,上面展示的每個函數組合器都是可以通過fold來實現的。我們來看一些示例。
| 1 2 3 4 5 6 7 8 | def ourMap(numbers: List[Int], fn: Int => Int): List[Int] = { ??numbers.foldRight(List[Int]()) { (x: Int, xs: List[Int]) => ????fn(x) :: xs ??} } scala> ourMap(numbers, timesTwo(_)) res0: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) |
為什么要List[Int]?因為Scala還不能聰明到知道你需要在一個空的Int列表上來進行累加。
如何處理好Map?
我們上面所展示的所有函數組合器都能都Map進行處理。Map可以當作是由鍵值對組成的列表,這樣你寫的函數就可以對Map里的key和value進行處理。
| 1 2 | scala> val extensions = Map("steve" -> 100, "bob" -> 101, "joe" -> 201) extensions: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101), (joe,201)) |
現在過濾出所有分機號碼小于200的元素。
| 1 2 | scala> extensions.filter((namePhone: (String, Int)) => namePhone._2 < 200) res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101)) |
?
因為你拿到的是一個元組,所以你不得不通過它們的位置來取得對應的key和value,太惡心了!
幸運的是,我們實際上可以用一個模式匹配來優雅地獲取key和value。
| 1 2 | scala> extensions.filter({case (name, extension) => extension < 200}) res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101)) |
為什么這樣也可以呢?為什么可以直接傳入一個局部的模式匹配呢?
這個內容就留在下周吧!
英文原文:?Scala School,翻譯:ImportNew?–?朱偉杰
本文鏈接:http://www.importnew.com/3673.html
總結
- 上一篇: Source Code Collecti
- 下一篇: 各种编程语言的深度学习库整理