【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)
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)が使えるようになったので、上手く使い分けていきましょう。