QuickCheck 中是否有 "there exists" 量词?
Is there a "there exists" quantifier in QuickCheck?
forAll
量词 returns 和 属性 检查所有测试用例是否通过。有没有一种方法可以定义一个 "there exists" 量词 returns 一个 属性 来检查它是否至少通过了一个测试用例?
我不知道QuickCheck中是否存在类似的东西。也不相信它会有帮助,但这是另一回事。因为我们有:
∃ x . P x <=> ¬ ¬ ∃ x . P x <=> ¬ ∀ x . ¬ P x
我想您可以设置它,以便您的测试用例测试否定,如果它失败,则意味着您已经存在。 HTH.
通过枚举测试存在性会更可靠:SmallCheck, LeanCheck, FEAT.
如果您必须使用随机生成,QuickCheck 中有一些间接方法。
expectFailure
似乎是一个很好的否定候选者。然而它不完全是,因为它不是内合的,所以你必须以其他方式否定 属性,比如 not
,如果你的 属性 实际上是布尔值。
exists :: Gen a -> (a -> Bool) -> Property
exists gen prop = once $ expectFailure (forAll gen (not . prop))
不过,这会将崩溃视为失败,因此即使您可能出乎意料,测试也会通过。
存在量化是一种析取,而 QuickCheck 有 disjoin
。只要做一个足够大的分离。
exists :: Gen a -> (a -> Property) -> Property
exists gen prop = once $ disjoin $ replicate 10000 $ forAll gen prop
但是当找不到很好的例子时,你会被反例淹没。
也许最好自己重写 forAll
的逻辑以避免调用 counterexample
.
更简单地说,您始终可以编写自己的 属性 作为生成器以获得适当的量化。
exists :: Gen a -> (a -> Bool) -> Property
exists gen prop = property (exists' 1000 gen prop)
exists' :: Int -> Gen a -> (a -> Bool) -> Gen Bool
exists' 0 _ _ = return False
exists' n gen prop = do
a <- gen
if prop a
then return True
else exists' (n - 1) gen prop
手动执行此操作也有 属性 如果 prop
意外崩溃,它会立即报告为失败,这与以前的方法相反。
编辑
因此,如果您只有 (a -> Property)
而不是 (a -> Bool)
,那么要获得好的结果似乎要棘手得多,因为检查 属性 是否成功并非易事。一个正确的方法是搞乱 QuickCheck 的内部结构,可能类似于 disjoin
。这是一个快速技巧
当没有找到(反例)示例时,使用 mapResult
使 disjoin
的输出静音。
覆盖 size 参数,因为如果您的 witnesses 是非平凡的,那么如果大小太小或太大都不会被发现。
import Test.QuickCheck
import Test.QuickCheck.Property as P
-- Retry n times.
exists :: Testable prop => Int -> Gen a -> (a -> prop) -> Property
exists n gen prop =
mapResult (\r -> r {P.reason = "No witness found.", P.callbacks = []}) $
once $
disjoin $
replicate n $ do
a <- gen
return (prop a)
main = quickCheck $ exists 100
(resize 5 arbitrary)
(\x -> (x :: Integer) == 2)
forAll
量词 returns 和 属性 检查所有测试用例是否通过。有没有一种方法可以定义一个 "there exists" 量词 returns 一个 属性 来检查它是否至少通过了一个测试用例?
我不知道QuickCheck中是否存在类似的东西。也不相信它会有帮助,但这是另一回事。因为我们有:
∃ x . P x <=> ¬ ¬ ∃ x . P x <=> ¬ ∀ x . ¬ P x
我想您可以设置它,以便您的测试用例测试否定,如果它失败,则意味着您已经存在。 HTH.
通过枚举测试存在性会更可靠:SmallCheck, LeanCheck, FEAT.
如果您必须使用随机生成,QuickCheck 中有一些间接方法。
expectFailure
似乎是一个很好的否定候选者。然而它不完全是,因为它不是内合的,所以你必须以其他方式否定 属性,比如 not
,如果你的 属性 实际上是布尔值。
exists :: Gen a -> (a -> Bool) -> Property
exists gen prop = once $ expectFailure (forAll gen (not . prop))
不过,这会将崩溃视为失败,因此即使您可能出乎意料,测试也会通过。
存在量化是一种析取,而 QuickCheck 有 disjoin
。只要做一个足够大的分离。
exists :: Gen a -> (a -> Property) -> Property
exists gen prop = once $ disjoin $ replicate 10000 $ forAll gen prop
但是当找不到很好的例子时,你会被反例淹没。
也许最好自己重写 forAll
的逻辑以避免调用 counterexample
.
更简单地说,您始终可以编写自己的 属性 作为生成器以获得适当的量化。
exists :: Gen a -> (a -> Bool) -> Property
exists gen prop = property (exists' 1000 gen prop)
exists' :: Int -> Gen a -> (a -> Bool) -> Gen Bool
exists' 0 _ _ = return False
exists' n gen prop = do
a <- gen
if prop a
then return True
else exists' (n - 1) gen prop
手动执行此操作也有 属性 如果 prop
意外崩溃,它会立即报告为失败,这与以前的方法相反。
编辑
因此,如果您只有 (a -> Property)
而不是 (a -> Bool)
,那么要获得好的结果似乎要棘手得多,因为检查 属性 是否成功并非易事。一个正确的方法是搞乱 QuickCheck 的内部结构,可能类似于 disjoin
。这是一个快速技巧
当没有找到(反例)示例时,使用
mapResult
使disjoin
的输出静音。覆盖 size 参数,因为如果您的 witnesses 是非平凡的,那么如果大小太小或太大都不会被发现。
import Test.QuickCheck
import Test.QuickCheck.Property as P
-- Retry n times.
exists :: Testable prop => Int -> Gen a -> (a -> prop) -> Property
exists n gen prop =
mapResult (\r -> r {P.reason = "No witness found.", P.callbacks = []}) $
once $
disjoin $
replicate n $ do
a <- gen
return (prop a)
main = quickCheck $ exists 100
(resize 5 arbitrary)
(\x -> (x :: Integer) == 2)