gpt4 book ai didi

scala - 正数的性质不应缩减为负数

转载 作者:行者123 更新时间:2023-12-04 17:43:12 27 4
gpt4 key购买 nike

在 ScalaCheck 中,我有一个属性测试,它使用一个正整数生成器,当它失败时 ScalaCheck 将收缩为一个非正值。

收缩应该有助于找到最少的失败案例。缩小到所需范围之外的值是令人困惑和无益的。这是一个已知错误,请参阅 ScalaCheck issue #129 Gen.suchThat not respected by shrinking

是否可以在范围内定义我自己的收缩实例,以便它只会收缩为正整数?

例子

这是一个最小的属性测试:

class ShrinkProp extends Properties("Shrink") {
property("posNum[Int]") = {
Prop.forAll(Gen.posNum[Int]) { _: Int =>
Prop.falsified
}
}
}

这通常会导致 ScalaCheck 将参数缩小为零:

[info] Done compiling.[info] ! Shrink.posNum[Int]: Falsified after 0 passed tests.[info] > ARG_0: 0[info] > ARG_0_ORIGINAL: 1[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Or worse, it could on occasion shrink to a negative value:

[info] ! Shrink.posNum[Int]: Falsified after 5 passed tests.[info] > ARG_0: -1[info] > ARG_0_ORIGINAL: 3[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Disable shrinking

One solution is shutting off shrinking with forAllNoShrink:

class ShrinkProp extends Properties("Shrink") {
property("posNum[Int]") = {
Prop.forAllNoShrink(Gen.posNum[Int]) { _: Int =>
Prop.falsified
}
}
}

结果是没有收缩:

[info] ! Shrink.posNum[Int]: Falsified after 0 passed tests.[info] > ARG_0: 1[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Adding guards

Another alternative is adding a guard to the test, so that he shrunk values or just skipped:

import Prop.BooleanOperators

class ShrinkProp extends Properties("Shrink") {
property("posNum[Int]") = {
Prop.forAll(Gen.posNum[Int]) { x: Int =>
(x >= 1) ==> Prop.falsified
}
}
}

是否有替代方法来禁用收缩和添加守卫?

最佳答案

ScalaCheck 中没有正整数收缩器。您必须自己编写。

概览

Shrink 需要定义为属性测试范围内的隐式。然后 Prop.forAll 将找到正确的 Shrink 类(如果它在范围内并且具有未通过测试的值的适当类型签名)。

从根本上说,Shrink 实例是一个将失败值 x 转换为“收缩”值流的函数。它的类型签名大致是:

trait Shrink[T] {
def shrink(x: T): Stream[T]
}

可以用伴生对象的apply方法定义一个Shrink,大致是这样的:

object Shrink {
def apply[T](s: T => Stream[T]): Shrink[T] = {
new Shrink[T] {
def shrink(x: T): Stream[T] = s(x)
}
}
}

答案:收缩正整数

正整数的收缩器是一个Stream,它通过将值减半来收缩以通过二进制搜索找到最小的失败案例,但在达到零之前停止:

class ShrinkProp extends Properties("Shrink") {

implicit val posIntShrinker: Shrink[Int] = Shrink { x: Int =>
Stream.iterate(x / 2) { x: Int =>
x / 2
}.takeWhile { x: Int =>
x > 0 // Avoid zero.
}
}

property("posNum[Int]") = {
Prop.forAll(Gen.posNum[Int]) { _: Int =>
Prop.falsified
}
}
}

证明失败有效:

[info] ! Shrink.posNum[Int]: Falsified after 6 passed tests.[info] > ARG_0: 2[info] > ARG_0_ORIGINAL: 4[info] Failed: Total 1, Failed 1, Errors 0, Passed 0

Even better, you could write a property to verify your shrinker behaves as it should:

property("posIntShrinker") = {
Prop.forAll { x: Int =>
val shrunk = Shrink.shrink(x)
Prop.atLeastOne(
(x >= 2) ==> shrunk.size > 0,
(x <= 1) ==> shrunk.isEmpty
)
}
}
[info] + Shrink.posIntShrinker: OK, passed 100 tests.[info] Failed: Total 1, Failed 0, Errors 0, Passed 1

写一个通用的正数 Shrink 会很好,它可以收缩其他类型的数字,比如 Long、浮点类型和 BigDecimal.

关于scala - 正数的性质不应缩减为负数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53483695/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com