2012年6月29日金曜日

[Scala] 条件付きFor文

単純に繰り返すだけではなくいろいろな機能があるので、それらも見てみよう。
今回は条件付きFor文。

for文に条件を指定することで適用対象をフィルタすることができる。
val a = Array(1, 2, 3, 4);
for (i: Int <- a if i % 2 == 0) print(i);
実行結果は、
24
この機能はこんな風にもかける。
val a = Array(1, 2, 3, 4);
a.withFilter((i: Int) => i % 2 == 0).foreach(i => print(i))
これだけを見ると、withFilter()でフィルタされたArrayが返ってきて、 それにforeachをしているように見えるがちょっと違う。
続けないでちゃんと書くと、
val a = Array(1, 2, 3, 4);
val filtered : FilterMonadic[Int, Array[Int]] 
    = a.withFilter((i: Int) => i % 2 == 0);
filtered.foreach((i: Int) => print(i))
こんな感じ。
実際に、a.withFilter() の結果として帰ってきているのは、
print (filtered.getClass());
で見ると、scala.collection.TraversableLike$WithFilter であることがわかる。

ちなみに、TraversableLike$WithFilter#foreach の実装を見ると、
def foreach[U](f: A => U): Unit =
  for (x <- self)
    if (p(x)) f(x)
となっている。

つまり、withFilter()で帰ってきているのはデータ(Array)とフィルタ条件を持ったObjectで、そのforeach()の中で条件付きfor文を実行しているということになる。

結論: 最初から、条件付きfor文を使えばいいじゃん。


foreach構文を使うためには対象のオブジェクトは
  def foreach[U](f: A => U): Unit 
メソッドを実装する必要があった。
foreach - if 構文を使うためには、
def withFilter(p: A => Boolean): FilterMonadic[A, Repr]
を実装する必要がある。
object ForIfSample {
  def main(args: Array[String]) {
    val array : Array[Int] = Array[Int](1,2);
    val fe = new ForEachIfable[Int, Array[Int]](array);
    for (i: Int <- fe if i % 2 == 0) print(i);
  }
}

class ForEachIfable[A, Repr <: Array[A] ](col: Repr) {
  self=>;
  def repr: Repr = col;
  
  def foreach[U](f: A => U): Unit = {
    println("Here is ForEachIfable#foreach");
    for (x : A <- repr)  print(x);
  }

  def withFilter(p: A => Boolean): FilterMonadic[A, Repr] = {
    println("Here is ForEachIfable#withFilter");
    new WithFilter(p);  
  }
  
  // 以下のWithFilterクラスはTraversable.WithFilterと同じ。
  
  class WithFilter(p: A => Boolean) extends FilterMonadic[A, Repr] {

    def map[B, That](f: A => B)
               (implicit bf: CanBuildFrom[Repr, B, That]): That = {
      val b = bf(repr)
      for (x <- self) if (p(x)) b += f(x)
      b.result
    }
    def flatMap[B, That](f: A => GenTraversableOnce[B])
               (implicit bf: CanBuildFrom[Repr, B, That]): That = {
      val b = bf(repr)
      for (x <- self)
        if (p(x)) b ++= f(x).seq
      b.result
    }
    
    def foreach[U](f: A => U): Unit=
        for (x : A <- self) if (p(x)) f(x)
        
    def withFilter(q: A => Boolean): WithFilter =
        new WithFilter(x => p(x) && q(x))  
  }
}
withFilter()が返すFilterMonadicの実装はTraversable.WithFilterクラスの実装をそのまま使っている。 そのため、ForEachIfableがやや無理やりになっている。
実行結果:
Here is ForEachIfable#withFilter
Here is ForEachIfable#foreach
12
確かに、ForEachIfable#foreach、withFilterが実行されていることがわかる。

0 件のコメント:

コメントを投稿