【问题标题】:Testing Recursive Data Structure测试递归数据结构
【发布时间】:2017-08-06 05:26:38
【问题描述】:

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,但我不确定。

【问题讨论】:

    标签: scala scalacheck property-based-testing


    【解决方案1】:

    在您的列表示例中,堆栈溢出的概率非常低——如果存在的话。原因——以及与树示例的不同之处——是您一次只使用一个元素。

    假设你的堆栈会在 1000 个元素后爆炸,发生这种情况的概率大约是 1/(2^1000),这是一个非常小的数字。

    【讨论】:

      【解决方案2】:

      由于生成器是递归的,很可能会导致堆栈溢出错误。问题在于oneOf() 在选择探索路径时是随机的;您的随机数生成器驱动树扩展。

      我发现我可以使用权重来获得我想要的深度的树。我相信我和frequency() 一起玩是为了让正确的重量发挥作用。

      【讨论】:

      • The issue is really that oneOf() is random in its selection of a path to explore。在上面的Tree 示例中,oneOf(gen1, gen2, ...) 将在可变参数列表中生成每个gen*,不是吗?如果是这样,那会导致无限递归?
      猜你喜欢
      • 2021-04-17
      • 1970-01-01
      • 2018-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-18
      相关资源
      最近更新 更多