ScalaCheck 生成 BST
ScalaCheck generate BST
我正在尝试使用 ScalaCheck 为 BST 创建一个 Gen,但是当我调用 .sample 方法时它给了我 java.lang.NullPointerException。我哪里错了?
sealed trait Tree
case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree
import org.scalacheck._
import Gen._
import Arbitrary.arbitrary
case class GenerateBST() {
def genValue(left: Tree, right: Tree): Gen[Int] = (left, right) match {
case (Node(_, _, min), Node(_, _, max)) => arbitrary[Int].suchThat(x => x > min && x < max)
case (Node(_, _, min), Leaf) => arbitrary[Int].suchThat(x => x > min)
case (Leaf, Node(_, _, max)) => arbitrary[Int].suchThat(x => x < max)
case (Leaf, Leaf) => arbitrary[Int]
}
val genNode = for {
left <- genTree
right <- genTree
v <- genValue(left, right)
} yield Node(left, right, v)
def genTree: Gen[Tree] = oneOf(const(Leaf), genNode)
}
GenerateBST().genTree.sample
由于您为递归数据类型递归定义生成器的方式,您需要在顶部使用 Gen.lzy
:
def genTree: Gen[Tree] = Gen.lzy(oneOf(const(Leaf), genNode))
作为不相关的旁注,在您的生成器定义中使用 suchThat
通常应该只是最后的手段。这意味着 sample
经常会失败(大约三分之一的时间是固定版本的代码),更重要的是,如果有一天你想创建导致 Tree
的任意函数,你将会看到很多可怕的 org.scalacheck.Gen$RetrievalError: couldn't generate value
异常。
在这种情况下,您可以很容易地避免 suchThat
,方法是使用 Gen.chooseNum
并在顺序错误的情况下交换左右两侧:
sealed trait Tree
case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree
import org.scalacheck.{ Arbitrary, Gen }
object GenerateBST {
def swapIfNeeded(l: Tree, r: Tree): (Tree, Tree) = (l, r) match {
// If the two trees don't have space between them, we bump one and recheck:
case (Node(_, _, x), n @ Node(_, _, y)) if math.abs(x - y) <= 1 =>
swapIfNeeded(l, n.copy(v = y + 1))
// If the values are in the wrong order, swap:
case (Node(_, _, x), Node(_, _, y)) if x > y => (r, l)
// Otherwise do nothing:
case (_, _) => (l, r)
}
def genValue(left: Tree, right: Tree): Gen[Int] = (left, right) match {
case (Node(_, _, min), Node(_, _, max)) => Gen.chooseNum(min + 1, max - 1)
case (Node(_, _, min), Leaf) => Gen.chooseNum(min + 1, Int.MaxValue)
case (Leaf, Node(_, _, max)) => Gen.chooseNum(Int.MinValue, max - 1)
case (Leaf, Leaf) => Arbitrary.arbitrary[Int]
}
val genNode = for {
l0 <- genTree
r0 <- genTree
(left, right) = swapIfNeeded(l0, r0)
v <- genValue(left, right)
} yield Node(left, right, v)
def genTree: Gen[Tree] = Gen.lzy(Gen.oneOf(Gen.const(Leaf), genNode))
}
现在您可以使用 Arbitrary[Whatever => Tree]
而不必担心生成器故障:
scala> implicit val arbTree: Arbitrary[Tree] = Arbitrary(GenerateBST.genTree)
arbTree: org.scalacheck.Arbitrary[Tree] = org.scalacheck.ArbitraryLowPriority$$anon@606abb0e
scala> val f = Arbitrary.arbitrary[Int => Tree].sample.get
f: Int => Tree = org.scalacheck.GenArities$$Lambda09/289518656@13eefeaf
scala> f(1)
res0: Tree = Leaf
scala> f(2)
res1: Tree = Node(Leaf,Leaf,-20313200)
scala> f(3)
res2: Tree = Leaf
scala> f(4)
res3: Tree = Node(Node(Leaf,Leaf,-850041807),Leaf,-1)
我正在尝试使用 ScalaCheck 为 BST 创建一个 Gen,但是当我调用 .sample 方法时它给了我 java.lang.NullPointerException。我哪里错了?
sealed trait Tree
case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree
import org.scalacheck._
import Gen._
import Arbitrary.arbitrary
case class GenerateBST() {
def genValue(left: Tree, right: Tree): Gen[Int] = (left, right) match {
case (Node(_, _, min), Node(_, _, max)) => arbitrary[Int].suchThat(x => x > min && x < max)
case (Node(_, _, min), Leaf) => arbitrary[Int].suchThat(x => x > min)
case (Leaf, Node(_, _, max)) => arbitrary[Int].suchThat(x => x < max)
case (Leaf, Leaf) => arbitrary[Int]
}
val genNode = for {
left <- genTree
right <- genTree
v <- genValue(left, right)
} yield Node(left, right, v)
def genTree: Gen[Tree] = oneOf(const(Leaf), genNode)
}
GenerateBST().genTree.sample
由于您为递归数据类型递归定义生成器的方式,您需要在顶部使用 Gen.lzy
:
def genTree: Gen[Tree] = Gen.lzy(oneOf(const(Leaf), genNode))
作为不相关的旁注,在您的生成器定义中使用 suchThat
通常应该只是最后的手段。这意味着 sample
经常会失败(大约三分之一的时间是固定版本的代码),更重要的是,如果有一天你想创建导致 Tree
的任意函数,你将会看到很多可怕的 org.scalacheck.Gen$RetrievalError: couldn't generate value
异常。
在这种情况下,您可以很容易地避免 suchThat
,方法是使用 Gen.chooseNum
并在顺序错误的情况下交换左右两侧:
sealed trait Tree
case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree
import org.scalacheck.{ Arbitrary, Gen }
object GenerateBST {
def swapIfNeeded(l: Tree, r: Tree): (Tree, Tree) = (l, r) match {
// If the two trees don't have space between them, we bump one and recheck:
case (Node(_, _, x), n @ Node(_, _, y)) if math.abs(x - y) <= 1 =>
swapIfNeeded(l, n.copy(v = y + 1))
// If the values are in the wrong order, swap:
case (Node(_, _, x), Node(_, _, y)) if x > y => (r, l)
// Otherwise do nothing:
case (_, _) => (l, r)
}
def genValue(left: Tree, right: Tree): Gen[Int] = (left, right) match {
case (Node(_, _, min), Node(_, _, max)) => Gen.chooseNum(min + 1, max - 1)
case (Node(_, _, min), Leaf) => Gen.chooseNum(min + 1, Int.MaxValue)
case (Leaf, Node(_, _, max)) => Gen.chooseNum(Int.MinValue, max - 1)
case (Leaf, Leaf) => Arbitrary.arbitrary[Int]
}
val genNode = for {
l0 <- genTree
r0 <- genTree
(left, right) = swapIfNeeded(l0, r0)
v <- genValue(left, right)
} yield Node(left, right, v)
def genTree: Gen[Tree] = Gen.lzy(Gen.oneOf(Gen.const(Leaf), genNode))
}
现在您可以使用 Arbitrary[Whatever => Tree]
而不必担心生成器故障:
scala> implicit val arbTree: Arbitrary[Tree] = Arbitrary(GenerateBST.genTree)
arbTree: org.scalacheck.Arbitrary[Tree] = org.scalacheck.ArbitraryLowPriority$$anon@606abb0e
scala> val f = Arbitrary.arbitrary[Int => Tree].sample.get
f: Int => Tree = org.scalacheck.GenArities$$Lambda09/289518656@13eefeaf
scala> f(1)
res0: Tree = Leaf
scala> f(2)
res1: Tree = Node(Leaf,Leaf,-20313200)
scala> f(3)
res2: Tree = Leaf
scala> f(4)
res3: Tree = Node(Node(Leaf,Leaf,-850041807),Leaf,-1)