ScalaCheck: Полное руководство объясняет, как создавать генераторы для рекурсивных структур данных.
Во-первых, он определяет структуру данных:
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
, но я не уверен.