LL脳がscalaの勉強を始めたよ その58


Scalaコップ本の17章をやっていきますよー、今回は集合とマップの前半として概要をやりますよー

基本はイミュータブルです( ー`дー´)キリッ

これまでやってきたとおり、Scalaのコレクションではほとんどがimmutableとmutableの両方を持っているものの、immutableなものを使うのがScala的!ということで単純に呼び出された場合はimmutableなオブジェクトが作られました。コレは3章でやったとおり集合(Set)やマップ(Map)も同様ですねー

この「基本はimmutable」ってルールはScalaソースファイルに暗黙にインポートされるPredefオブジェクトによってこんなふうに決められているみたいです

object Predef {
  // デフォルトでimmutableが呼ばれるように定義されてますな
  type Set[T] = scala.collection.immutable.Set[T]
  type Map[K,V] = scala.collection.immutable.Map[K,V]
  val Set = scala.collection.immutable.Set
  val Map = scala.collection.immutable.Map
  //.....
}

上記の定義より単純にSetやMapを呼び出した場合はPredef.SetやPredef.Mapを呼び出すことになって、結果的にimmutableなSetやMapを呼び出すことになるみたいですねー。それとtypeキーワードについては20章でやるそうなので一旦放置しますよ。

ちなみにmutableなのを呼び出すときはこんな感じです

// ミュータブルSetを呼び出しますよ
scala> import scala.collection.mutable.Set
import scala.collection.mutable.Set
// 集合定義です
scala> Set(1,2,3,4,5)
res0: scala.collection.mutable.Set[Int] = Set(5, 3, 1, 4, 2)

// ミュータブルMapを呼び出しますよー 
scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map
// Mapの定義です
scala> Map(1 -> 'a', 2->'b', 3->'c')
res1: scala.collection.mutable.Map[Int,Char] = Map(2 -> b, 1 -> a, 3 -> c)

ちなみにこういう呼出をすればimmutableなSetやMapも同時に使えるみたいです

// mutableパッケージで読み込み
scala>  import scala.collection.mutable      
import scala.collection.mutable

// ミュータブル版を使う場合はこう
scala> mutable.Set(1,2,3)
res4: scala.collection.mutable.Set[Int] = Set(3, 1, 2)
// イミュータブルなものも使えますよ
scala> Set(1,2,3)        
res5: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

// ミュータブルMapです
scala> mutable.Map(1->'a',2->'b',3->'c')
res6: scala.collection.mutable.Map[Int,Char] = Map(2 -> b, 1 -> a, 3 -> c)
// イミュータブルなMapです
scala> Map(1->'a',2->'b',3->'c')        
res7: scala.collection.immutable.Map[Int,Char] = Map(1 -> a, 2 -> b, 3 -> c)

集合の使い方

Setは乱暴に言ってしまえば重複がないコレクションですね。正確には==で比較して同じとされる要素が1つしかないというみたいですが…まあ、そんな感じで(´・ω・`)...例えば文章中の単語の種類の数え上げなんかに使えるそうデス。

とりあえずサンプルをやってみますかねー

// ミュータブル版Setを利用するのでインポートします
scala> import scala.collection.mutable
import scala.collection.mutable

// 文章を定義しますよー
// ハイパーいい加減な7つ(重複こみ)の単語で構成された文章です
scala> val text="Hello world is worldwide in hello world!"
text: java.lang.String = Hello world is worldwide in hello world!

// 文章を単語で区切って配列に格納しますよー。区切り文字は' '(スペース) ! , . デス 
scala> val wordsArray = text.split("[ !,.]")
wordsArray: Array[java.lang.String] = Array(Hello, world, is, worldwide, in, hello, world)
 
// 空の集合を作成します
scala> val words = mutable.Set.empty[String]
words: scala.collection.mutable.Set[String] = Set()

// 単語を小文字に変換してSetの要素として格納します
scala> for(word <- wordsArray) words += word.toLowerCase

// 作成されたSetです。単語は全部で5つでした(´・ω・`)
scala> words
res9: scala.collection.mutable.Set[String] = Set(in, is, world, worldwide, hello)
Setの操作

Setの操作でよくつかわれるものをザザッとやっていきますよー

//// イミュータブル編
// Setの構築
scala> val numSet = Set(1,2,3)
numSet: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

// 要素を追加しますよ
scala> numSet + 5
res10: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 5)
// 要素を取り除きますよ
scala> numSet - 5
res11: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

// 複数の要素を追加しますよ
scala> numSet ++ List(5,6)
res12: scala.collection.immutable.Set[Int] = Set(5, 3, 1, 6, 2)
// 複数の要素を取り除きますよ
scala> numSet -- List(1,2)
res13: scala.collection.immutable.Set[Int] = Set(3)

// 2つの集合の積集合(共通)をとりますよ
scala> numSet ** Set(1,3,5,7)
res14: scala.collection.immutable.Set[Int] = Set(1, 3)

// 集合の要素数をとりますよ
scala> numSet.size
res15: Int = 3

// 集合に要素が含まれているかどうかを判定しますよ
scala> numSet.contains(3)
res17: Boolean = true

//// ミュータブル編
// ミュータブル版のパッケージを読み込みます
scala> import scala.collection.mutable
import scala.collection.mutable

// 空のミュータブルSetを作りますよ
scala> val wordSet = mutable.Set.empty[String]
wordSet: scala.collection.mutable.Set[String] = Set()

// 要素を追加します
scala> wordSet += "the"
// 追加結果です
scala> wordSet
res20: scala.collection.mutable.Set[String] = Set(the)
// 要素を取り除きます
scala> wordSet -= "the"
// 取り除いた結果です
scala> wordSet         
res22: scala.collection.mutable.Set[String] = Set()

// 複数要素を追加します
scala> wordSet ++= List("do", "re", "mi")
scala> wordSet                                  
res24: scala.collection.mutable.Set[String] = Set(do, re, mi)
// 複数要素を取り除きます 
scala> wordSet --= List("do", "re")      
scala> wordSet                     
res26: scala.collection.mutable.Set[String] = Set(mi)

// 全ての要素を取り除きます
scala> wordSet.clear
scala> wordSet      
res28: scala.collection.mutable.Set[String] = Set()

マップの使い方

マップは乱暴に言ってしまえば任意の種類のキーを使える配列みたいなもんですかね、PHP連想配列と思っておけば間違いなさそうです。

とりあえずキーと値の対応関係を保持できるマップの操作をいろいろとやってみますかね

// 今回はミュータブル版のMapを使いますよ
scala> import scala.collection.mutable
import scala.collection.mutable

// 空のミュータブルMapを作成します
// MapはString => Intの形式になります
scala> val map = mutable.Map.empty[String, Int]
map: scala.collection.mutable.Map[String,Int] = Map()

// キーがhelloで値が1の要素を追加します
scala> map("hello") = 1
// キーがworldで値が2の要素を追加します
scala> map("world") = 2

// 構成されたMapはこんな感じです
scala> map
res31: scala.collection.mutable.Map[String,Int] = Map(hello -> 1, world -> 2)

// Mapの要素にアクセスします
scala> map("hello")
res32: Int = 1

ミュータブルMapを使った基本操作はこんな感じですねー

Mapを使ったサンプル

Mapを使ったもう少し複雑なサンプルをやってみますかね、先程Setでやったサンプルに似たようなもので、文章の中の単語がそれぞれ幾つずつ含まれているか数える処理を書いてみますよ

// 今回もミュータブル版Mapを使いますよ
scala> import scala.collection.mutable
import scala.collection.mutable

// いい加減極まりない文章を定義しますよ
scala> val text = "Hello World is worldwide in hello world!"
text: java.lang.String = Hello World is worldwide in hello world!

// 空にミュータブルMapを定義します
scala> val counts = mutable.Map.empty[String, Int]
counts: scala.collection.mutable.Map[String,Int] = Map()

// 文章を区切り文字で単語に分解してカウントアップしますよ
scala> for(rawWord <- text.split("[ ,.!]+")){
         // 単語を小文字にします
     |   val word = rawWord.toLowerCase
         // カウントアップ元の数字を取り出しますよ
     |   val oldCount = {
           // もし該当単語がMapに登録されていればその数字を
     |     if (counts.contains(word)) counts(word)
           // なければ0を返します
     |     else 0
     |   }
         // 数字を1加えてMapに値を格納します
     |   counts += (word -> (oldCount + 1))
     | }
// 構成されたMapです
scala> counts
res36: scala.collection.mutable.Map[String,Int] = Map(hello -> 2, worldwide -> 1, world -> 2, is -> 1, in -> 1)
Mapの操作

Setと同様にMapについても記法操作をズラズラと乗っけますよ

//// イミュータブル編
// マップを構築しますよ
scala> val numMap = Map("i"->1, "ii"->2)
numMap: scala.collection.immutable.Map[java.lang.String,Int] = Map(i -> 1, ii -> 2)

// マップに要素を追加します
scala> numMap + ("vi"->6)
res37: scala.collection.immutable.Map[java.lang.String,Int] = Map(i -> 1, ii -> 2, vi -> 6)
// マップから要素を取り除きます
scala> numMap - "ii"
res38: scala.collection.immutable.Map[java.lang.String,Int] = Map(i -> 1)

// マップに複数要素を追加しますよ
scala> numMap ++ List("iii"->3, "v"->5)
res39: scala.collection.immutable.Map[java.lang.String,Int] = Map(i -> 1, ii -> 2, iii -> 3, v -> 5)
// マップから複数要素を取り除きますよ
scala> numMap -- List("i", "ii")       
res40: scala.collection.immutable.Map[java.lang.String,Int] = Map()

// Mapの要素数を取得します
scala> numMap.size
res41: Int = 2

// Mapの要素の存在チェックをします
scala> numMap.contains("ii")
res42: Boolean = true

// Mapの要素を指定して取り出します
scala> numMap("ii")
res43: Int = 2

// マップのキーで構成されたIteratorを生成します
scala> numMap.keys
res44: Iterator[java.lang.String] = non-empty iterator
// イテレータを使ってみますよー
scala> val numMapIt = numMap.keys      
numMapIt: Iterator[java.lang.String] = non-empty iterator
// 次の要素が存在します
scala> numMapIt.hasNext
res45: Boolean = true
// 次の要素を取得します
scala> numMapIt.next   
res46: java.lang.String = i

// Mapのキー全体をSetとして取得します
scala> numMap.keySet
res47: scala.collection.Set[java.lang.String] = Set(i, ii)

// Mapの値で構成されたIteratorを生成します
scala> numMap.values
res48: Iterator[Int] = non-empty iterator
// イテレーターってみます
scala> val numMapIt = numMap.values
numMapIt: Iterator[Int] = non-empty iterator
// 次の要素の存在チェックです
scala> numMapIt.hasNext
res49: Boolean = true
// 次の要素を取り出しますよ
scala> numMapIt.next   
res50: Int = 1

// Mapの空チェックをします 
scala> numMap.isEmpty
res51: Boolean = false

//// ミュータブル編です
scala> import scala.collection.mutable
import scala.collection.mutable

// 空のミュータブルMapを生成します
scala> val wordMap = mutable.Map.empty[String, Int]    
wordMap: scala.collection.mutable.Map[String,Int] = Map()

// Mapの値を追加します
scala> wordMap += ("one"->1)                           
// 追加結果です
scala> wordMap                                         
res58: scala.collection.mutable.Map[String,Int] = Map(one -> 1)
// Mapから値を取り除きます 
scala> wordMap -= "one"                                
// 結果ですよー
scala> wordMap         
res60: scala.collection.mutable.Map[String,Int] = Map()

// Mapに複数の値を追加してみますよ
scala> wordMap ++= List("one"->1, "two"->2, "three"->3)
scala> wordMap                                         
res62: scala.collection.mutable.Map[String,Int] = Map(one -> 1, two -> 2, three -> 3)
// Mapから複数の値を取り除きますよ
scala> wordMap --= List("one","two")                   
scala> wordMap                      
res64: scala.collection.mutable.Map[String,Int] = Map(three -> 3)

// Mapの要素を全て取り除きますよー
scala> wordMap.clear
scala> wordMap      
res66: scala.collection.mutable.Map[String,Int] = Map()

いじょー

今回はSetとMapの基本的な使い方をザザッとやりました。次回はもう少し深い部分に掘り下げていきますよー