Scala 片段2:List的操作符魔法

fairjm發表於2014-12-11

本文翻譯自:Scala snippets 2: List symbol magic
fairjm@ituring


Scala的每一個操作符都可以是函式,所以過載操作符(實際上不是真正的過載操作符,畢竟操作符都已經是方法了)是一件非常簡單並且在很多庫中都能看見的事。在這個片段中,我們將探討一些讓列表操作更加簡單的方法過載。

讓我們從++操作符開始。首先,就像我們一直做的那樣,讓我們來建立一個列表:

scala> val list = 0 until 10 toList
list: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val list2 = 10 to 0 by -1 toList
list2: List[Int] = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)  

然後大致瀏覽下來自 http://www.scala-lang.org/api/2.11.1/index.html#scala.collection.immutable.List 的一些操作:

第一個操作符是++。我們可以用這個操作符把兩個列表連線在一起,並且返回一個新的列表:

scala> val list3 = list ++ list2
list3: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

scala> val list3 = list2 ++ list
list3: List[Int] = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)  

注意你並不需要保證列表是相同型別的。Scala會自動選擇最相關的父類:

scala> val list1 = 0 to 10 toList
list1: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> val list2 = 10 to 0 by -1 toList
list2: List[Int] = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

scala> val list3 = list1.asInstanceOf[List[Double]]
list3: List[Double] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> list3 ++ list2
res4: List[AnyVal] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)  

你可以看到最後的返回是AnyVal型別的列表,這是對於IntDouble最常見的父類。

既然我們已經看過++了,讓我們來看看和++看起來最相似的++:。這個操作符和++有著相同的語義,不同的地方是結果的型別在++中取決於左運算元而這個操作符取決於右運算元:

scala> vector1
res14: Vector[Int] = Vector(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)

scala> list1
res15: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> vector1 ++ list1
res16: scala.collection.immutable.Vector[Int] = Vector(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> vector1 ++: list1
res17: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)  

接下來的兩個操作符:++:可以讓我們向後和向前對列表增加新的元素:

scala> 999 +: list1
res27: List[Int] = List(999, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> list1 :+ 999
res28: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 999)  

還有什麼?:::::都是對列表之前增加一些東西的函式。::操作符增加單個元素,而:::操作符增加一個列表。所以基本上他們是和+:++操作一樣的。主要的區別是+:++可以被用於Traversable,但:::::只能用於列表。

scala> 11 +: list1
res38: List[Int] = List(11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> list1
res39: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> 11 +: list1
res40: List[Int] = List(11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> list2
res41: List[Int] = List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

scala> list1 ::: list2
res43: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)  

:\/:用於摺疊操作(看這裡)。:\從右至左摺疊,而/:從左至右:

scala> list1
res50: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> (1 /: list1)((r,i) => {println(i);i+r})
0
1
2
3
4
5
6
7
8
9
10
res51: Int = 56

scala> (list1 :\ 1)((i,r) => {println(i);i+r})
10
9
8
7
6
5
4
3
2
1
0
res52: Int = 56    

你可以看到摺疊的方向決定了元素是從前往後還是從後往前處理的。

這就是這個片段的全部。

相關文章