SSブログ

ScalaCheck を試す (4) データ構造の生成と分布の制御 [Scala]

ScalaCheck 1.0-RC1 がアナウンス [1] されました。

前回 [2] のカスタムジェネレータ作成では整数のジェネレータを扱いましたが、もちろん構造を持つデータを生成するジェネレータを定義することもできます。
こうした場合は基本的にはその型の文法をフォローするような形でジェネレータを書くことになるでしょう。例えば整数のリストの場合は

List[Int] ::= Nil
List[Int] ::= Int :: List[Int]

のような2つのプロダクションからなる文法を持つと考えられるのでジェネレータはこの文法に対応して

def arbInts: Gen[List[Int]] =
  oneOf(value(Nil), for (head <- arbitrary[Int]; rest <- arbInts) yield head::rest)

と書けばよさそうです。ここで oneOf は引数に2つのジェネレータを取りそのどちらかを生成するジェネレータを返すメソッドです。また value は常に引数の値を返すジェネレータです。

さて、このジェネレータには分かりにくい罠があります。oneOf は引数のどちらかを「等確率」で生成します。ということはこの arbInts が生成するテストケースはその半分が Nil で、4分の1が1要素のリストで8分の1が2要素のリストで…となるのです。意図や場合にもよるでしょうが、これはまあ偏りのある分布だと考えられます。

oneOf メソッドの代わりに frequency メソッドを使うと引数に重み付けをして分布を制御することができるようになります。ちなみにこれは ScalaCheck には 1.0-RC1 で新たに追加されたメソッドだと思います。

def arbInts2: Gen[List[Int]] =
   frequency(1 -> value(Nil), 4 -> {for (head <- arbitrary[Int]; rest <- arbInts) yield head::rest})

frequency は任意の数の (Int, T) のタプルを引数にとり、最初の整数によって重み付けをして値を選び出します。この場合 Nil が 1 に対してコンスは 4 の確率で値を選び出します。ちなみに 1 -> value(Nil) は (1, value(Nil)) と等価な Scala の syntactic sugar です。

[1] http://www.nabble.com/-scala--ANNOUNCE:-ScalaCheck-1.0-RC1-t4380903.html
[2] http://blog.so-net.ne.jp/rainyday/2007-08-26-1


nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。