ScalaCheck: The Definitive Guide解释了如何为递归数据结构创建生成器。
首先,它定义了数据结构:
trait Tree[T] {
def size: Int
}
case class Leaf[T](item: T) extends Tree[T] {
def size = 1
}
case class Node[T] (children: List[Tree[T]]) extends Tree[T] {
def size = children.map(_.size).sum
}
接下来,它会显示Gen[Tree[A]]
代码:
import org.scalacheck.Gen
import org.scalacheck.Gen.{oneOf, listOf, lzy}
def genTree[T](genT: Gen[T]): Gen[Tree[T]] = lzy {
oneOf(genLeaf(genT), genNode(genT))
}
def genLeaf[T](genT: Gen[T]): Gen[Leaf[T]] =
genT.map(Leaf(_))
def genNode[T](genT: Gen[T]): Gen[Node[T]] = for {
children <listOf(
genTree(genT))
} yield Node(children)
对于上面的生成器,本书演示,调用它可以产生StackOverflowError
:
scala> genIntTree.sample
res0: Option[Tree[Int]] = Some(Leaf(2147483648))
scala> genIntTree.sample
res1: Option[Tree[Int]] = Some(Leaf(0))
scala> genIntTree.sample
java.lang.StackOverflowError
at org.scalacheck.Gen$$anonfun$1$$anonfun$apply...
给出以下MyList
数据结构:
sealed abstract class MyList[+A]
case class Cons[+A](elem: A, rest: MyList[A]) extends MyList[A]
case object Empty extends MyList[Nothing]
以下发电机:
def genList[A](gen: Gen[A]): Gen[MyList[A]] =
lzy { oneOf(genCons(gen), Gen.const(Empty)) }
def genCons[A](gen: Gen[A]): Gen[MyList[A]] = for {
list <- genList(gen)
a <- gen
} yield Cons(a, list)
我的理解是Gen[Tree[A]]
对listOf
的使用是StackOverflowError
的责任。
但是,StackOverflowError
代码的生成器中是否可以Gen[MyList[A]]
?
我猜这是genList
足够Cons
,但我不确定。
答案 0 :(得分:2)
因为生成器是递归的,所以很可能导致堆栈溢出错误。问题实际上是oneOf()
在选择探索路径时是随机的;随机数生成器驱动树扩展。
我发现我可以使用加权来获得我想要的树木。我相信我和frequency()
一起玩,以获得合适的权重。
答案 1 :(得分:0)
在列表示例中,堆栈溢出的可能性非常低 - 如果存在的话。原因 - 以及与树示例的区别 - 是您一次只能使用一个元素。
假设您的筹码在1000个元素之后会爆炸,这种情况发生的概率大约为1 /(2 ^ 1000),这是一个非常小的数字。