iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 29
2
Software Development

認識scala系列 第 29

Scala day 29 (some collection method)

map

對 collection 每個元素個別處理 :

scala> val lst = List("Daniel","Apple","Mac","Taipei","Jack","Hello world","Banana","scala")
lst: List[String] = List(Daniel, Apple, Mac, Taipei, Jack, Hello world, Banana, scala)

scala> lst.map(name => name.length)
res16: List[Int] = List(6, 5, 3, 6, 4, 11, 6, 5)

scala> lst.map(_.length)
res15: List[Int] = List(6, 5, 3, 6, 4, 11, 6, 5)

reduce

將 collection 的元素收集處理,第一次先處理兩個元素,產生的解果再跟下個元素處理,最後回傳跟元素一樣型態的值 :

scala> lst.map(_.length).reduce((num1:Int,num2:Int) => num1 + num2)
res17: Int = 46

scala> lst.map(name => name.length)
res16: List[Int] = List(6, 5, 3, 6, 4, 11, 6, 5)

flatten

flatten 可以將 collection 裡的 collections 轉換成一個 collection :

scala> val lst = List(List(1,2), List(3,4,List(5,6)))
lst: List[List[Any]] = List(List(1, 2), List(3, 4, List(5, 6)))

scala> lst.flatten
res26: List[Any] = List(1, 2, 3, 4, List(5, 6))

flatMap

flatMap 會先執行 map 之後再處理 flatten,相當於兩個的結合 :
下面例子會先對 List 裡的 每個 List 做 distinct,再 flatten 成一個 collection :

scala> val lst = List(List(1,2,1,2), List(5,3,4,1,5), List(2,4,6,7))
lst: List[List[Int]] = List(List(1, 2, 1, 2), List(5, 3, 4, 1, 5), List(2, 4, 6, 7))

scala> lst.flatMap(_.distinct)
res33: List[Int] = List(1, 2, 5, 3, 4, 1, 2, 4, 6, 7)

scala> lst.map(_.distinct).flatten
res34: List[Int] = List(1, 2, 5, 3, 4, 1, 2, 4, 6, 7)

distinct

distinct 可將重複的元素變成一個 :

scala> val lst = List(List(1,2), List(3,4,1,5), List(2,4,6,7))
lst: List[List[Int]] = List(List(1, 2), List(3, 4, 1, 5), List(2, 4, 6, 7))

scala> lst.flatten.distinct
res27: List[Int] = List(1, 2, 3, 4, 5, 6, 7)

sortWith

scala> lst.sortWith(_ < _)
res20: List[String] = List(Apple, Banana, Daniel, Hello world, Jack, Mac, Taipei, scala)

scala> lst.sortWith(_ > _)
res21: List[String] = List(scala, Taipei, Mac, Jack, Hello world, Daniel, Banana, Apple)

Option、map、flatMap、sum :

在介紹 List 的時候有先介紹過這範例,不過這邊再來複習一次,會比較有感覺.
定義一個 function 將 String 轉成 Option[Int] 的 type :

scala> def toInt(s: String): Option[Int] = {
     |   try {
     |     Some(Integer.parseInt(s.trim))
     |   } catch {
     |     case e: Exception => None
     |   }
     | }
toInt: (s: String)Option[Int]

然後定義一組有數字及文字的 Seq[String] 的 collection,然後透過 map 對裡面每個元素做上面定義的 toInt 方法,
可以轉成 Int 的會回傳 Some(Int) 不行的回傳 None

scala> val strings = Seq("1", "2", "foo", "3", "bar")
strings: Seq[String] = List(1, 2, foo, 3, bar)

scala> strings.map(toInt)
res35: Seq[Option[Int]] = List(Some(1), Some(2), None, Some(3), None)

這時候透過 flatMap 展開後,None 物件會被過濾掉,而 Some(Int) 會轉成 Int :

scala> strings.flatMap(toInt)
res36: Seq[Int] = List(1, 2, 3)

最後透過 sum 將元素的值做加總 :

scala> strings.flatMap(toInt).sum
res37: Int = 6

fold

fold 有兩個參數,第一個是代初始值,第二的是 lambda expressions 就是元素要處理的事 :
下面例子初始值是 0,然後執行順序參考下面 :

scala> val numbers = List(5, 4, 8, 6, 2, 3)
numbers: List[Int] = List(5, 4, 8, 6, 2, 3)

scala> numbers.fold(0) { (z, i) =>
     |   println(z + " , " + i)
     |   z + i
     | }
0 , 5 //初始值 0 跟 第一個元素 5 相加
5 , 4 //(0 + 5) 上一個的結果跟下個元素 4 做相加
9 , 8 //上一個的結果(9)跟下個元素 4 做相加,以此類推...
17 , 6
23 , 2
25 , 3
res38: Int = 28

fold 之外還有另外兩個 foldLeft、foldRight,foldLeft 是從左邊開始,foldLeft 是從右邊開始,fold 順序不一定 :
foldLeft 範例,初始參數改成 1,從左邊開始加

scala> val numbers = List(5, 4, 8, 6, 2, 3)
numbers: List[Int] = List(5, 4, 8, 6, 2, 3)

scala> numbers.foldLeft(1) { (z, i) =>
     |   println(z + " , " + i)
     |   z + i
     | }
1 , 5 //初始值 1 跟左邊第一個元素 5 相加
6 , 4 //(1 + 5) 上一個的結果 6 跟左邊第二個元素 4 作相加...
10 , 8
18 , 6
24 , 2
26 , 3
res45: Int = 29

foldRight 範例,初始參數改成 2,從右邊開始加 :

scala> numbers.foldRight(2) { (z, i) =>
     |   println(z + " , " + i)
     |   z + i
     | }
3 , 2 //初始值 2 跟右邊第一個元素 3 相加
2 , 5 //(2 + 3) 上一個的結果 5 跟右邊第二個元素 2 作相加...
6 , 7
8 , 13
4 , 21
5 , 25
res46: Int = 30

最後試著了解這段 code 吧 :

scala> val lst = List("Daniel","Apple","Mac","Taipei","Jack","Hello world","Banana","scala")
lst: List[String] = List(Daniel, Apple, Mac, Taipei, Jack, Hello world, Banana, scala)

scala> lst.map(_.length).filter(_ > 4).foldLeft(List.empty[Int]) {
     |   (s,ele) => if(s.contains(ele)) List(ele) else (s :+ ele)
     | }.reduce(_ + _)
res47: Int = 11

總結


  • scala collection 提供非常多的 api,這邊只是先列幾個範例,可以再多找些資訊學習.
  • 上述 Code 解析 :
scala> lst.map(_.length) // 將每個元素轉成該元素的長度,然後產生一組新的 List[Int]
res48: List[Int] = List(6, 5, 3, 6, 4, 11, 6, 5)

scala> List(6, 5, 3, 6, 4, 11, 6, 5).filter(_ > 4)
res49: List[Int] = List(6, 5, 6, 11, 6, 5) // 透過 filter 只取得 > 4 的元素,再產生一組新的 List[Int]

scala> List(6, 5, 6, 11, 6, 5).foldLeft(List.empty[Int]) {
     |   (s,ele) => if(s.contains(ele)) List(ele) else (s :+ ele)
     | } // 透過 foldLeft 初始值參數是一個空的 List 也就是(s),然後判斷左邊第一個元素 6,是否包含在 s 裡,不在就加入回傳新的 List,否則就不加入回傳原來的 List.可去除重複的元素,也可用 distinct.
res50: List[Int] = List(6, 5)

scala> List(6, 5).reduce(_ + _) //接著透過 reduce 將每個元素做相加回傳跟元素一樣的type Int
res51: Int = 11

scala> List(6, 5).sum //使用 sum 將元素做加總
res52: Int = 11


上一篇
Scala day 28 (json)
下一篇
Scala day 30 (Functional Programming)
系列文
認識scala30

尚未有邦友留言

立即登入留言