我看到 scalacheck 似乎是一个非常明显的错误,如果它真的存在,我看不到人们如何将它用于递归数据结构。
该程序StackOverflowError
在 scalacheck 接管之前失败,同时构造Arbitrary
值。请注意,s 的Tree
类型和生成器Tree
是从这个 scalacheck 教程中逐字获取的。
package treegen
import org.scalacheck._
import Prop._
class TreeProperties extends Properties("Tree") {
trait Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf(x: Int) extends Tree
val ints = Gen.choose(-100, 100)
def leafs: Gen[Leaf] = for {
x <- ints
} yield Leaf(x)
def nodes: Gen[Node] = for {
left <- trees
right <- trees
} yield Node(left, right)
def trees: Gen[Tree] = Gen.oneOf(leafs, nodes)
implicit lazy val arbTree: Arbitrary[Tree] = Arbitrary(trees)
property("vacuous") = forAll { t: Tree => true }
}
object Main extends App {
(new TreeProperties).check
}
奇怪的是,不应该影响任何事情的更改似乎会改变程序以使其正常工作。例如,如果您将 的定义更改trees
为此,它会毫无问题地通过:
def trees: Gen[Tree] = for {
x <- Gen.oneOf(0, 1)
t <- if (x == 0) {leafs} else {nodes}
} yield t
更奇怪的是,如果您更改二叉树结构,以便将值存储在Node
s 上而不是Leaf
s 上,并将leafs
andnodes
定义更改为:
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)
然后它也可以正常工作。
这里发生了什么?为什么构造Arbitrary
值最初会导致堆栈溢出?为什么 scalacheck 生成器似乎对不应该影响生成器控制流的微小变化如此敏感?
为什么我上面的表达与oneOf(0, 1)
原来的表达不完全一样oneOf(leafs, nodes)
?