2015年11月30日月曜日

Scala Tips/Map + Monoid

ちょっと忘れがちですが、ScalazではMapもMonoidとして定義されています。

これが結構便利なのを再認識したのでメモしておきます。

カウンター

サイトごとのPV数をカウントするような用途でMonoidが便利に使えます。

以下のMapを考えます。Mapの値側の型がIntでありMonoidなので、Map全体もMonoidとして機能します。

MapをMonoidとして使用する場合もMapの作成は通常と同じです。

scala> var c = Map[String, Int]("www.abc.com" -> 1)
c: scala.collection.immutable.Map[String,Int] = Map()

サイトごとのカウンタをMapで管理しています。このカウンタを上げる処理は以下のように書くことができます。

scala> c = c.get("www.abc.com") match {
     | case Some(s) => c + ("www.abc.com" -> (s + 1))
     | case None => c + ("www.abc.com" -> 1)
     | }

この処理をMonoidを使って書くと以下になります。Monoidの演算子|+|で、サイトと足したいカウンタ数の組によるMapを足し込みます。

scala> c = c |+| Map("www.abc.com" -> 2)
c: scala.collection.immutable.Map[String,Int] = Map(www.abc.com -> 3)

scala> c = c |+| Map("www.abc.com" -> 5)
c: scala.collection.immutable.Map[String,Int] = Map(www.abc.com -> 8)

別のサイトのMapを足し込むと、Mapの別のエントリとして管理されます。

scala> c = c |+| Map("www.xyz.com" -> 3)
c = c |+| Map("www.xyz.com" -> 3)
c: scala.collection.immutable.Map[String,Int] = Map(www.xyz.com -> 3, www.abc.com -> 8)

リスト

同様の処理はListやVectorでも使用することができます。

以下のMapを考えます。サイトごとのタグの集まりを管理するMapをイメージしています。

scala> var r = Map[String, Vector[String]]("www.abc.com" -> Vector("spring"))
var r = Map(www.abc.com -> Vector(spring))

Monoidの演算子|+|で、サイトと追加したいタグの組によるMapを足し込みます。

scala> r = r |+| Map("www.abc.com" -> Vector("summer"))
r: scala.collection.immutable.Map[String,Vector[String]] = Map(www.abc.com -> Vector(spring, summer))

別のサイトのMapを足し込むと、Mapの別のエントリとして管理されます。

scala> r = r |+| Map("www.xyz.com" -> Vector("autumn"))
r: scala.collection.immutable.Map[String,Vector[String]] = Map(www.xyz.com -> Vector(autumn), www.abc.com -> Vector(spring, summer))

まとめ

値の集まりをMapで管理する処理は、そう頻繁に使うものでもないので毎回必要となる度に実現方式やロジックを考えることになりがちでした。Scalaの場合、mutableなMapの場合はMultiMapを使う方法もありますが、immutableなMapはこの方法は取れないようです。その場合はそれなりのロジックを組む必要が出てきます。

ということでWebで実現方式を調べていた所、以下のページにScalazを使う方法が載っていたというわけです。

いわれてみればこの方法があったか、ということで最近はこのパターンを愛用しています。

Monoid Index

昔調べた記事です。

Monoidに関する2012年ごろのブログのまとめです。

OptionをMonoidで使うと便利という記事。

Scalaプログラミングの肝はMonoidにあり、というわけで結構Monoidは調べていたつもりだったのですが、大きな応用が抜けていました。他にも何か大きな応用があるかもしれません。

諸元

  • Java 1.7.0_75
  • Scala 2.11.6
  • Scalaz 7.1.0