乱数を生成するfor..yield

30分プログラム、その418。mapやらfilterを定義すれば、俺々データ構造でもforが使えるらしいので、試してみる。
というわけで、for(n <- stream) ..とするだけで乱数を生成できるストリームを作ってみた。
ちなみにscala.Randomはdeprecatedで、scala.util.Randomを使うべきらしいけど、ボクのScala処理系は古いらしくてscala.util.Randomは無かったのでdeprecatedを使ってる。

使い方

// 長さ5のストリーム
scala> val r = new RandomStream(5,new Random)
r: RandomStream = RandomStream@f7545d

// printしてみる
scala> for(n <- r) println(n)
190666319
-1796022525
666838077
671772833
1415902003

// printするたびに値が違う
scala> for(n <- r) println(n)
1132383051
-494624490
1443618707
1804687541
-78546838

// yieldもできる
scala> for(n <- r) yield n
res63: List[Int] = List(-795386642, -1038537116, -1497499699, 1275316298, -1514816727)

// 正の乱数だけを取り出す
scala> for(n <- r if n > 0) yield n
res64: List[Int] = List(357014503)

ソースコード

class RandomStream(n : Int,random : Random) {
  private def next = new RandomStream(n-1,random)
 
  def map[A](f : Int => A) : List[A] =
    if(n == 0)
      List()
    else
      f(random nextInt)::(next map f)

  def filter(f : Int => Boolean) : List[Int] = {
    if(n == 0)
      List()
    else {
      val r = random.nextInt()
      if(f(r))
	r::(next filter f)
      else
	next filter f
    }
  }
     
  def foreach(f : Int => Unit) : Unit = 
    if(n == 0)
      ()
    else{
      f(random nextInt)
      next foreach(f)
    }
}