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)