2012年6月22日金曜日

Scala Tips / multiplication

モノイド演算は加算に限るわけではなく、条件を満たす任意の演算が対象となります。

ScalazではIntなどの整数値のモノイド演算は加算として定義しています。数値に対するモノイド演算で最も利用されそうなのが加算なので妥当な選択ですが、場合によっては乗算などの演算を数値のモノイド演算として使用したい場合もあります。

そこでScalazでは、乗算をモノイド演算として定義したMultiplicationというオブジェクトを提供しています。Multiplicationは以下の7種類が用意されています。

数値Multiplication
ByteByteMutiplication
CharCharMutiplication
ShortShortMutiplication
IntIntMutiplication
LongLongMutiplication
BigIntBigIntMutiplication
BigIntegerBigIntegerMutiplication

以下ではIntのMultiplicationであるIntMultiplicationを例にMultiplicationの使い方について説明していきます。

Intのモノイド演算

まずIntのモノイド演算を確認します。

scala> 3 |+| 5
res71: Int = 8

結果は「3+5」で8になりました。ScalazにおけるIntは加算を二項演算子とするモノイドであることが確認できました。

IntMultiplication

Scalazはモノイド演算を乗算にしたIntとしてIntMultiplicationオブジェクトを提供しています。

IntMultiplicationの生成方法は2つです。

1つはmultiplication関数を使う方法です。

scala> val a = multiplication(3)
a: scalaz.IntMultiplication = 3

もう一つはIntの∏メソッドを使う方法です。「∏」は「N-ARY PRODUCT」を示すUnicode文字です。

scala> val b = 5 ∏
b: scalaz.IntMultiplication = 5

モノイド演算

先ほど生成した2つのIntMultiplicatinオブジェクトを演算子「|+|」でモノイド演算してみます。

scala> val c = a |+| b
c: scalaz.IntMultiplication = 15

結果は「3×5」で15になりました。IntMultiplicatinは、乗算を二項演算子とするモノイドであることが確認できました。

Intに戻す

IntMultiplicatinをIntに戻すには属性valueを使用します。

scala> a.value
res73: Int = 3

scala> b.value
res74: Int = 5

scala> c.value
res75: Int = 15

モノイド畳込み

モノイドの重要なユースケースが畳込みです。モノイドを畳込み対象にすることで色々な指定を省略することができます。(Validation (25) - fold monoid)

IntのListに対してモノイドの畳込みをすると総和が得られます。foldメソッドなどを使った通常の畳込みと比べると初期値と畳込み演算の両方を省略することができます。

scala> List(1, 2, 3, 4, 5).sumr
res63: Int = 15

IntのListに対して乗算で畳込みを普通に書くと以下になります。

scala> List(1, 2, 3, 4, 5).foldRight(1)((x, a) => a * x)
res66: Int = 120

これをIntMultiplicationを用いて、乗算を二項演算子とするモノイド演算として実装すると以下になります。

IntのListをIntMultiplicationのListに変換後、モノイド演算による畳込みをしていますが、IntMultiplicationのモノイド演算が乗算なので、List内のIntをすべて掛けた結果の120が返ってきています。

scala> List(1, 2, 3, 4, 5).map(multiplication).sumr
res64: scalaz.IntMultiplication = 120

上の演算結果はIntMultiplicationなので、さらにIntに戻すには以下のように最後にvalueをアクセスすればOKです。

scala> List(1, 2, 3, 4, 5).map(multiplication).sumr.value
res77: Int = 120
モノイドの零元

モノイドが畳込みと相性が良い理由の一つが、モノイドの零元を畳込みの初期値に使えることがあります。

Intの零元は以下のように0になります。

scala> mzero[Int]
res68: Int = 0

一方IntMultiplicationの零元は以下のように1になります。

scala> mzero[IntMultiplication]
res67: scalaz.IntMultiplication = 1

加算による畳込みの場合の初期値として0、乗算による畳込みの場合の初期値として1を使うのは当たり前といえば当たり前なので、この指定を自動的に行なってくれるのはプログラミング的にとてもありがたいことです。

foldMap

ScalazはMonoidに対する畳込みを行うメソッドとしてfoldMapメソッドを用意しています。foldMapメソッドは元の値をMonoidに変換する関数を引数に取り、変換後のMonoidに対して畳み込みを行います。

IntのListに対して乗算による畳込みを行う場合は以下のようになります。基本的には前述したmapメソッドでMonoidに変換後sumrメソッドでモノイド畳込みを行うのと同じになります。

scala> List(1, 2, 3, 4, 5).foldMap(multiplication)
res69: scalaz.IntMultiplication = 120

計算結果のIntMultiplicationからIntを取り出すには属性valueをアクセスします。

scala> List(1, 2, 3, 4, 5).foldMap(multiplication).value
res70: Int = 120

ノート

「∏」(220F)はN-ARY PRODUCTという名前のUnicode文字です。直積を表すUnicode文字のようです。

直積はパイ(π)の大文字である「Π」(03A0)でも表現できると思いますが、数学記号の直積として使う場合は「∏」(220F)を使うのがUnicodeの流儀ということのようです。

「∏」(220F)の分かりやすい説明は以下のページにもありました。

諸元

  • Scala 2.9.2
  • Scalaz 6.0.4

0 件のコメント:

コメントを投稿