理解ScalaChecks'';生成大小';

理解ScalaChecks'';生成大小';,scala,scalacheck,Scala,Scalacheck,ScalaCheck的GenAPI解释lazy val size: def大小[T](f:(整数)⇒ Gen[T]:Gen[T] 创建可以访问其生成大小的生成器 请看以下示例: import org.scalacheck.Gen.{sized, posNum} scala> sized( s => { println(s); posNum[Int] }) res12: org.scalacheck.Gen[Int] = org.scalacheck.Gen$$anon$3@6a07

ScalaCheck的
Gen
API解释
lazy val size

def大小[T](f:(整数)⇒ Gen[T]:Gen[T]

创建可以访问其生成大小的生成器

请看以下示例:

import org.scalacheck.Gen.{sized, posNum}

scala> sized( s => { println(s); posNum[Int] })
res12: org.scalacheck.Gen[Int] = org.scalacheck.Gen$$anon$3@6a071cc0

scala> res12.sample
100
res13: Option[Int] = Some(12)

scala> res12.sample
100
res14: Option[Int] = Some(40)

生成大小
,即上述输出中的100是什么意思?

生成大小是生成器将生成的结果数。
size
方法只允许您编写知道自己大小的生成器,以便您可以将该信息用作生成内容的一个因素

例如,此生成器(由此)生成两个数字列表,其中1/3为正,2/3为负:

import org.scalacheck.Gen
import org.scalacheck.Prop.forAll

val myGen = Gen.sized { size =>
  val positiveNumbers = size / 3
  val negativeNumbers = size * 2 / 3
  for {
    posNumList <- Gen.listOfN(positiveNumbers, Gen.posNum[Int])
    negNumList <- Gen.listOfN(negativeNumbers, Gen.posNum[Int] map (n => -n))
  } yield (size, posNumList, negNumList)
}

check {
  forAll(myGen) {
    case (genSize, posN, negN) =>
     posN.length == genSize / 3 && negN.length == genSize * 2 / 3
  }
}
import org.scalacheck.Gen
导入org.scalacheck.Prop.forAll
val myGen=Gen.size{size=>
val正枚举数=大小/3
val NegativeEnumbers=大小*2/3
为了{
波斯努姆利斯特
posN.length==genSize/3&&negN.length==genSize*2/3
}
}

因此,有点像Scala集合中的
zipWithIndex
size
只提供元信息来帮助您完成所需操作。

size
提供对Scalacheck的“size”参数的访问。此参数表示生成器生成的值应该有多大。此参数在以下几种情况下很有用:

  • 您希望限制生成的值的数量,以便生成属性,从而加快测试运行速度
  • 您需要将生成的数据放入外部约束中,比如检查字符串长度的表单验证器,或者对列进行限制的数据库
  • 您需要生成递归数据结构并在某个点终止
Gen.size
的配套功能是
Gen.resize
,它允许您更改生成器的大小,如
Gen.resize(10,Gen.alphaNumString)
,它将生成不超过十个字符的字母数字字符串

例如,大多数内置生成器以某种方式使用
大小
(这是列表和容器的所有生成器的基础):

[…]容器的大小由生成值时使用的size参数限定

一个简单的例子 要了解如何使用
Gen.size
,请查看“大小生成器”(《Scalacheck用户指南》)中的示例:

此生成器使用“大小”限制矩阵的尺寸,以便整个矩阵的条目数永远不会超过“大小”参数。换句话说,如果你的问题是100,那么生成的矩阵将有10行10列,总计100个条目

递归数据结构 “大小”对于确保递归数据结构的生成器终止特别有用。考虑下面的例子,它生成二进制树的实例,并使用<代码>大小<代码>限制每个分支的高度,以确保生成器终止在某个点:

import org.scalacheck.Gen
import org.scalacheck.Arbitrary.arbitrary

sealed abstract class Tree
case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree

val genLeaf = Gen.const(Leaf)
val genNode = for {
  v <- arbitrary[Int]
  left <- Gen.sized(h => Gen.resize(h/2, genTree))
  right <- Gen.sized(h => Gen.resize(h/2, genTree))
} yield Node(left, right, v)

def genTree: Gen[Tree] = Gen.sized { height =>
  if (height <= 0) {
    genLeaf
  } else {
    Gen.oneOf(genLeaf, genNode)
  }
}
此定义使用
frequency
使
None
不太有趣的情况比
Some
更有趣的情况更不可能发生

事实上,我们可以在上面的二叉树示例中结合
Gen.size
Gen.frequency
,使
genTree
更有可能生成“有趣”的节点,而不是“无聊”的叶子:

def genTree:Gen[Tree]=Gen.size{height=>
if(高度genLeaf,9->genNode)
}
}

谢谢您的详细回答。因此,使用Gen.frequency尝试终止递归数据结构的生成器有点困难,但可能会奏效?感谢您的更新-它消除了我的误解!关于使用
Gen.resize(n/2,…)
,这不意味着大多数生成的数据具有相同的深度吗。换句话说,如果我的初始
size
是100,那么每个测试递归结构将由深度8组成:
100->50->25->12->6->3->1->0
,不是吗?换句话说,如果
高度也相同,而不是使用惯用的“size”,我们可以传递一个“counter”int值,终止于@KevinMeredith,我再次编辑了我的问题。简而言之,大小是高度的上限,但不影响节点或叶的相似性。这些都是由
genTree
中的偏差单独控制的,即您是否使用其中一个或频率,以及频率的参数。至于计数器,是的,您可以这样做,如果您只想控制树的高度,但不影响节点中包含的数据的生成器,您可以这样做。
import org.scalacheck.Gen
import org.scalacheck.Arbitrary.arbitrary

sealed abstract class Tree
case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree

val genLeaf = Gen.const(Leaf)
val genNode = for {
  v <- arbitrary[Int]
  left <- Gen.sized(h => Gen.resize(h/2, genTree))
  right <- Gen.sized(h => Gen.resize(h/2, genTree))
} yield Node(left, right, v)

def genTree: Gen[Tree] = Gen.sized { height =>
  if (height <= 0) {
    genLeaf
  } else {
    Gen.oneOf(genLeaf, genNode)
  }
}
def option[T](g: Gen[T]): Gen[Option[T]] =
  frequency(1 -> const(None), 9 -> some(g))
def genTree: Gen[Tree] = Gen.sized { height =>
  if (height <= 0) {
    genLeaf
  } else {
    Gen.frequency(1 -> genLeaf, 9 -> genNode)
  }
}