ScalaCheck 的 Gen API docs 解释 lazy val sized :



看下面的例子:

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)

上面输出中的 generation size ,即 100 是什么意思?

最佳答案

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

  • 您想限制生成的值的数量,以便生成属性,从而加快测试运行速度。
  • 您需要将生成的数据拟合到外部约束中,例如检查字符串长度的表单验证器或对列进行限制的数据库。
  • 您需要生成递归数据结构并在某个时刻终止。
  • Gen.sized 的伴侣是 Gen.resize,它允许您更改生成器的大小,如 Gen.resize(10, Gen.alphaNumString) 将生成不超过 10 个字符的字母数字字符串。

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



    一个简单的例子

    要了解如何使用 Gen.size,请查看“大小生成器”中的示例( Generators ,Scalacheck 用户指南):
    def matrix[T](g: Gen[T]): Gen[Seq[Seq[T]]] = Gen.sized { size =>
      val side = scala.math.sqrt(size).asInstanceOf[Int]
      Gen.listOfN(side, Gen.listOfN(side, g))
    }
    

    这个生成器使用“size”来限制矩阵的维度,这样整个矩阵的条目永远不会超过“size”参数。换句话说,如果您的问题的大小为 100,则生成的矩阵将有 10 行和 10 列,总共有 100 个条目。

    递归数据结构

    “大小”对于确保递归数据结构的生成器终止特别有用。考虑以下示例,该示例生成二叉树的实例并使用 size 来限制每个分支的高度以确保生成器在某个点终止:
    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)
      }
    }
    

    请注意节点的生成器如何递归生成树,但只允许它们“大小”的一半。树生成器反过来只会在其大小耗尽时生成叶子。因此,生成器的“大小”是生成树高度的上限,确保生成器在某个点终止并且不会生成过大的树。

    请注意,在此示例中,大小仅为树的高度设置了 上限 。它不影响生成树的平衡或生成具有一定深度的树的可能性。这些仅取决于 genTree 中定义的偏差。

    使用 oneOf,每个子树都有 50% 的机会成为叶子,在这个分支结束树的生长,这使得生成一棵用尽“整个”大小的完整树有点不太可能。
    frequency(见下文)让你编码不同的偏差。在下面的例子中,节点比叶子更有可能,所以下面的生成器生成的树更有可能生长,但它仍然不太可能是完整的。

    与发电频率的关系
    Gen.frequency 用于不同的用例:您不会使用它来限制数据结构的深度或大小,而是为生成器的选择添加一定的偏差。看一下 Gen.option 的定义:
    def option[T](g: Gen[T]): Gen[Option[T]] =
      frequency(1 -> const(None), 9 -> some(g))
    

    此定义使用 frequency 使 None 不太有趣的情况比 Some 更有趣的情况更不可能。

    事实上,我们可以在上面的二叉树示例中结合 Gen.sizedGen.frequency 使 genTree 更有可能生成“有趣”的节点而不是“无聊”的叶子:
    def genTree: Gen[Tree] = Gen.sized { height =>
      if (height <= 0) {
        genLeaf
      } else {
        Gen.frequency(1 -> genLeaf, 9 -> genNode)
      }
    }
    

    关于scala - 理解 ScalaChecks 的 'generation size',我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42834516/

    10-16 21:37