scalacheck任意隐式和递归生成器
我看到scalacheck有一个非常明显的bug,如果它真的存在,我就看不出人们是如何将它用于递归数据结构的 在scalacheck接管之前,当构造scalacheck任意隐式和递归生成器,scala,recursive-datastructures,scalacheck,Scala,Recursive Datastructures,Scalacheck,我看到scalacheck有一个非常明显的bug,如果它真的存在,我就看不出人们是如何将它用于递归数据结构的 在scalacheck接管之前,当构造任意值时,此程序失败,出现一个StackOverflowerError。请注意,树类型和树s的生成器是逐字提取的 更奇怪的是,如果更改二叉树结构,使值存储在节点s而不是叶s上,并将叶和节点定义更改为: def leafs: Gen[Leaf] = Gen.value(Leaf()) def nodes: Gen[Node] = for {
任意
值时,此程序失败,出现一个StackOverflowerError
。请注意,树
类型和树
s的生成器是逐字提取的
更奇怪的是,如果更改二叉树结构,使值存储在节点
s而不是叶
s上,并将叶
和节点
定义更改为:
def leafs: Gen[Leaf] = Gen.value(Leaf())
def nodes: Gen[Node] = for {
x <- ints // Note: be sure to ask for x first, or it'll StackOverflow later, inside scalacheck code!
left <- trees
right <- trees
} yield Node(left, right, x)
def leafs:Gen[Leaf]=Gen.value(Leaf())
def节点:Gen[节点]=用于{
x问题在于,当Scala计算树
时,它会以无休止的递归结束,因为树
是根据自身定义的(通过节点
)。但是,当您将树
以外的其他表达式作为for表达式的第一部分放在节点
中时,Scala将延迟对for表达式其余部分的求值(以map
和flatMap
调用链包裹),并且不会发生无限递归
正如pedrofurla所说,如果
中的一个是非严格的,这可能不会发生(因为Scala不会立即计算参数)。但是,您可以使用Gen.lzy
明确说明懒散。lzy
接受任何生成器,并延迟对该生成器的求值,直到它真正被使用。因此,以下更改解决了您的问题:
def trees: Gen[Tree] = Gen.lzy(Gen.oneOf(leafs, nodes))
尽管上面下面的答案在程序启动时去掉了常量StackOverflowerError
,但在我实际要求scalacheck检查属性时,我仍然会在三次中打一次StackOverflowerError
。(我将上面的Main
更改为运行。检查40次,会看到它成功两次,然后由于堆栈溢出而失败,然后成功两次,以此类推。)
最后,我不得不将一个硬块放到递归的深度,我想这就是将来在递归数据结构上使用scalacheck时要做的:
def leafs: Gen[Leaf] = for {
x <- ints
} yield Leaf(x)
def genNode(level: Int): Gen[Node] = for {
left <- genTree(level)
right <- genTree(level)
} yield Node(left, right)
def genTree(level: Int): Gen[Tree] = if (level >= 100) {leafs}
else {leafs | genNode(level + 1)}
lazy val trees: Gen[Tree] = genTree(0)
def leafs:Gen[Leaf]=for{
Daniel Martin自己的答案中对方法的一个轻微概括是使用。类似(未经测试):
def genTree()=Gen.size{size=>genTree0(size)}
def genTree0(最大深度:Int)=
if(maxDepth==0)leafs else Gen.oneOf(leafs,genNode(maxDepth))
def genNode(maxDepth:Int)=用于{
depthL推测:是否第一个树总是计算两个参数,而使用的树总是计算两个参数,如果不计算呢?遗憾的是,其中一个确实很严格:在scalacheck代码中,这仍然有大约三分之一的时间失败,但它确实回答了我的问题,并且确实移动了堆栈溢出错误
从任意
对象的创建中退出,并进入其被评估的位置。接受这个答案,但我将分别发布我最终必须做的事情,以帮助通过web搜索找到它的人。
def trees: Gen[Tree] = Gen.lzy(Gen.oneOf(leafs, nodes))
def leafs: Gen[Leaf] = for {
x <- ints
} yield Leaf(x)
def genNode(level: Int): Gen[Node] = for {
left <- genTree(level)
right <- genTree(level)
} yield Node(left, right)
def genTree(level: Int): Gen[Tree] = if (level >= 100) {leafs}
else {leafs | genNode(level + 1)}
lazy val trees: Gen[Tree] = genTree(0)
def genTree() = Gen.sized { size => genTree0(size) }
def genTree0(maxDepth: Int) =
if (maxDepth == 0) leafs else Gen.oneOf(leafs, genNode(maxDepth))
def genNode(maxDepth: Int) = for {
depthL <- Gen.choose(0, maxDepth - 1)
depthR <- Gen.choose(0, maxDepth - 1)
left <- genTree0(depthL)
right <- genTree0(depthR)
} yield Node(left, right)
def leafs = for {
x <- ints
} yield Leaf(x)