【Scala】列、集合、マップ

前々回の続きです。
(http://bach.istc.kobe-u.ac.jp/lect/ProLang/org/scala-list.html)
今回は5.列~7.マップまで

列(Seq)

列はリストの上位クラスで、Seq用に定義したメソッドはListにも利用可能。
Seqはシーケンス(Sequence)の略。

したがって,特別な必要がない限りListではなくSeqを用いるほうが良いだろう

と言っているわりにSeqじゃなくてListを使っているので迷う

::,:::はSeqでは利用できない(代わりに:+,++を用いる)

scala> var seq = Seq(1,2,3,4)
seq: Seq[Int] = List(1, 2, 3, 4)

定義方法はListと同じ

scala> def s(seq: Seq[Int]) = seq.size
s: (seq: Seq[Int])Int
scala> s((1 to 5).toList)
res5: Int = 5

Seq用に作った関数も同じように使える。(toListがなくてもちゃんと返ってくる)

ほとんどListと同じなので、頭の片隅にでも

集合(Set)

Scalaでは集合も利用できる。

scala> var set = Set(2, 5, 2, 7, 1, 5)
set: scala.collection.immutable.Set[Int] = Set(2, 5, 7, 1)

集合なので同じ数字はまとめられる。
以下「scala.collection.immutable.」は省略します(長いので)

集合もリスト同様、様々なメソッドが用意されている。
その中でも、集合特有のメソッドをピックアップ

  • intersect(s) 共通集合を求める
scala> set.intersect(Set(4,1,2))
res: Set[Int] = Set(2, 1)

&でも同じことが出来る

scala> set&(Set(4,1,2))
res: Set[Int] = Set(2, 1)

ANDの集合版

  • union(s) 和集合を求める
scala> set.union(Set(4,1,2))
res6: Set[Int] = Set(5, 1, 2, 7, 4)

|で代用可

scala> set|(Set(4,1,2))
res: Set[Int] = Set(5, 1, 2, 7, 4)

OR集合

  • diff(s) 差集合を求める 
scala> set.diff(Set(4,1,2))
res: Set[Int] = Set(5, 7)

&~で代用

scala> set&~(Set(4,1,2))
res: Set[Int] = Set(5, 7)

集合の引き算

  • subsetOf(s) 部分集合かどうかの判定
scala> set.subsetOf(Set(4,1,2))
res: Boolean = false

scala> set.subsetOf(Set(7,1,2,5,8))
res: Boolean = true



また、toListでリストへの変換も可能

scala> var list = set.toList
list: List[Int] = List(2, 5, 7, 1)

toSetで集合に。

scala> var set = list.toSet
set: Set[Int] = Set(2, 5, 7, 1)


マップ (Map)

メソッドのmapとはまた別。
Mapの特徴は、キーで値(要素)を特定できる点。キーは一意(重複不可)。

定義方法は

scala> var map = Map("hamlet"->118, "ophelia"->31)
map: Map[String,Int] = Map(hamlet -> 118, ophelia ->31)

「->」の左辺がキーで、右辺が値。


また、キーと値をペアにして定義することもできる

scala> var map = Map(("hamlet",118),("ophelia",31))
map: Map[String,Int] = Map(hamlet -> 118, ophelia ->31)


キーが被っていた場合、後に設定した方に値が入る模様。

scala> var map = Map("hamlet"->118, "ophelia"->31, "ophelia"->21)
map: Map[String,Int] = Map(hamlet -> 118, ophelia ->21)



+によって新しく値を入れたマップを求められる

scala> map + ("horatio"->48)
res: Map[String,Int] = Map(hamlet -> 118, ophelia -> 31, horatio -> 48)

この時はペアで新しく値を入れることは出来ない。

また、これでは元のマップは変化していないので、変化させたマップを利用したい
場合は変数に代入する必要がある

scala> map = map + ("horatio"->48)
map: Map[String,Int] = Map(hamlet -> 118, ophelia ->31, horatio -> 48)


+=を使っても良い

scala> map += ("horatio"->60)

scala> map
res: Map[String,Int] = Map(hamlet -> 118, ophelia -> 31, horatio -> 60)

リンク先のページではval(定数)でmapを定義しているためかmapに再代入出来なかったため、こちらではmapの定義にvarを使っています。
これに限らずval定数に+=系は全部使えないよね、というお話

以下マップの主なメソッド

  • contains キーがマップに含まれているかどうか
scala> map.contains("horatio")
res: Boolean = true

値って書いてありましたけど、キーですね。

  • apply キーから値を取り出す
scala> map.apply("hamlet")
res: Int = 118

例のごとくマップ名そのものから取り出せる

scala> map("hamlet")
res: Int = 118

キーがない場合はエラーとなる。

  • getOrElse キーが設定されていなかった場合の値を指定できる
scala> map.getOrElse("hamlet",10)
res: Int = 118

scala> map.getOrElse("secon",0)
res: Int = 0

新しくマップにキーと値を設定するわけではないので注意。

  • keys キーの集合を求める
scala> map.keys
res: Iterable[String] = Set(hamlet, ophelia, horatio)


  • size キーの個数を求める
scala> map.size
res: Int = 3


  • filterKeys キーに対する条件を満たすキーとその値を求める
scala> map.filterKeys(_.size >= 7)
res: Map[String,Int] = Map(ophelia -> 31, horatio -> 60)


マップに直接filterを適用する場合はキーと値の対を引数とする関数を与える。

scala> map.filter(kv=>kv._2 <= 60)
res: scala.collection.immutable.Map[String,Int] = Map(ophelia -> 31, horatio -> 60)

これは値が60以下のものを求めている。
kv._2というのはkvの2番目の要素、すなわち値を取り出している(1番目はキー)

scala> map.filter(kv=>kv._2>= 60).filter(kv=>kv._1.size <= 6)
res: Map[String,Int] = Map(hamlet -> 118)

無理やりフィルターを2個つけたけど、上手く一つのfilterにまとめる方法はあるのだろうか

scala> map.filter{case (w,c) => c <= 60}
res: Map[String,Int] = Map(ophelia -> 31, horatio -> 60)

caseを使った無名関数を利用してもOK。caseを使う場合は{}で括る点に注意



これでMapとSetとList(Seq)が使えるようになったので、上手く使い分けていきましょう。