【问题标题】:ScalaCheck generate BSTScalaCheck 生成 BST
【发布时间】:2019-03-01 14:34:12
【问题描述】:

我正在尝试使用 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

【问题讨论】:

    标签: scala binary-search-tree scalacheck


    【解决方案1】:

    由于您为递归数据类型递归定义生成器的方式,您需要在顶部使用Gen.lzy

    def genTree: Gen[Tree] = Gen.lzy(oneOf(const(Leaf), genNode))
    

    作为不相关的旁注,在您的生成器定义中使用suchThat 通常应该只是最后的手段。这意味着sample 经常会失败(大约三分之一的时间使用你的代码的固定版本),更重要的是,如果你有一天想要创建导致Tree 的任意函数,你会看到一个很多可怕的org.scalacheck.Gen$RetrievalError: couldn't generate value 异常。

    在这种情况下,您可以通过使用Gen.chooseNum 并在它们的顺序错误时交换左右两侧来轻松避免suchThat

    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 =&gt; Tree] 而不必担心生成器故障:

    scala> implicit val arbTree: Arbitrary[Tree] = Arbitrary(GenerateBST.genTree)
    arbTree: org.scalacheck.Arbitrary[Tree] = org.scalacheck.ArbitraryLowPriority$$anon$1@606abb0e
    
    scala> val f = Arbitrary.arbitrary[Int => Tree].sample.get
    f: Int => Tree = org.scalacheck.GenArities$$Lambda$7109/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)
    

    【讨论】:

    • 嗨,我怎么有这个例外!属性评估引发异常。 > 例外:org.scalacheck.Gen$Choose$IllegalBoundsError:无效边界:low=1,high=-1 org.scalacheck.Gen$Choose$$anon$5.choose(Gen.scala:383) org.scalacheck.Gen$选择$$anon$5.choose(Gen.scala:381) org.scalacheck.Gen$Choose$$anon$7.choose(Gen.scala:423) org.scalacheck.Gen$.chooseNum(Gen.scala:822) $ line6.$read$$iw$$iw$GenerateBST$.genValue(:21) 我不明白为什么……你能帮帮我吗?
    • 哦,对了,如果它们相同,您将不得不撞到一个(而不是交换)。我会在一秒钟内更新答案。
    • 好的,范围问题已解决。
    • 您可能仍想对深度进行某种检查,因为genNode 可能会被重复调用(但这是您希望使用suchThat 版本或我的版本)。跨度>
    • 好的,但是这样我们并不总是创建正确的 BST,因为当我们碰撞时,我们不会检查更改是否涉及正确的孩子。如果右孩子值比父亲大一,我们就没有 BST。我说的对吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    相关资源
    最近更新 更多