Monoidを使ってみた
Semigroup(半群)に単位元を加えたものがMonoid(単位的半群)。Semigroupは結合法則を持つ。
ざっくり言うと「足し算のやり方と、ゼロはなんだ?」というのを持っていればMonoid。
import scalaz._ import Scalaz._ object HelloMonoid extends App { case class Hoge(cost: Int, cv: Int) implicit object HogeMonoid extends Monoid[Hoge] { def zero: Hoge = Hoge(0, 0) def append(r1: Hoge, r2: => Hoge): Hoge = Hoge(r1.cost + r2.cost, r1.cv + r2.cv) } Console println Hoge(100, 2) |+| Hoge(50, 3) //Hoge(150,5) val hs = Hoge(100, 2) :: Hoge(200, 3) :: Nil Console println hs.reduce(_ |+| _) //Hoge(300,5) Console println hs.foldLeft(HogeMonoid.zero)(_ |+| _) //Hoge(300,5) case class Foo(s: String, xs: List[Int]) implicit object FooMonoid extends Monoid[Foo] { def zero: Foo = Foo("", List.empty) def append(r1: Foo, r2: => Foo): Foo = Foo(s"${r1.s} and ${r2.s}", r1.xs ::: r2.xs) } val fs = Foo("apple", List(1, 2)) :: Foo("ornge", List(5, 9)) :: Foo("banana", List(0)) :: Nil Console println fs.reduce(_ |+| _) //Foo(apple and ornge and banana,List(1, 2, 5, 9, 0)) }
zipしてmap
// 100円のりんご、200円のもも、150円のレモンを // それぞれ5個、10個、15個購入した時の値段を求めるようなやーつ // とあるプログラミング問題で使ったのでメモ object zip { def main(args: Array[String]) { val point = List(100, 200, 150) val count = List(5, 10, 15) val result = point zip count map { case(p, c) => p * c } val total = result.reduce(_ + _) Console println total } }
curry化と関数オブジェクト
defで定義したメソッドは関数オブジェクトじゃないらしい。toStringした時に
(Function2トレイトとかのtoStringでそう実装されておる)
https://github.com/scala/scala/blob/2.11.x/src/library/scala/Function2.scala#L56
ここで関数オブジェクトをカリー化してみよう
scala> val func3 = (x1: Int, x2: Int, x3:Int) => x1 + x2 + x3
func2: (Int, Int, Int) => Int = <function3>
scala> func3.curried
res18: Int => (Int => (Int => Int)) = <function1>
問題ない。
def定義関数をカリー化してみよう
scala> def func3(x1: Int, x2: Int, x3:Int) = x1 + x2 + x3 func2: (x1: Int, x2: Int, x3: Int)Int scala> func3.curried <console>:15: error: missing arguments for method func3; follow this method with `_' if you want to treat it as a partially applied function func3.curried ^
ほう・・・
エラーが起きるので一度関数オブジェクトにする
scala> (func3 _) res34: (Int, Int, Int) => Int = <function3> scala> (func3 _).curried res31: Int => (Int => (Int => Int)) = <function1>
defで定義した関数をカリー化したいときは、 _ で一度関数オブジェクトにしてカリーかすればいい気がしたけどあっているのかな…?
あと、カリー化は引数の束縛と言うよりは1引数関数にすることで関数型プログラミングするときに都合がいい、っていう雰囲気がする。
ふつうのコトをふつうに
[0,100,200] というListを受け取って、MySQLに0〜99, 100〜199のidを検索するクエリを組みたい。
forを回してid1, id2で受け取って処理するっていう感じかーというC言語脳で考えつつ、値をスライディングしていくんだねというところに注目してscalaのコレクション探したらslidingって関数があった。
scala> List(1,2,3,4,5).sliding(2).foreach( x=> println( s"${x.head} : ${x.last}" ) ) 1 : 2 2 : 3 3 : 4 4 : 5
こんなかんじっすよね
scala> List(0,100,200).sliding(2).foreach( x=> println( s"(value >= ${x.head} and value < ${x.last})" ) ) (value >= 0 and value < 100) (value >= 100 and value < 200)
groupedもいい
scala> List(1,2,3,4,5).grouped(2).toList res34: List[List[Int]] = List(List(1, 2), List(3, 4), List(5))
ふつうのコトをふつうにこなす、コレクション(データ構造)を取り扱うためのアルゴリズムがScalaちゃんは豊富、って感じ。(多分関数型言語全般)
ネストしたリストを均す
ほかにもやり方ありそうだけど。
scala> List(List(1,2)).fold(List())(_:::_) res34: List[Int] = List(1, 2) scala> List(List(1,2), List(3,4)).fold(List())(_:::_) res35: List[Int] = List(1, 2, 3, 4)
これflattenでいいじゃん。。情弱さにしけた。
scala> List(List(1,2), List(3,4)).flatten res1: List[Int] = List(1, 2, 3, 4)