【问题标题】:Using a custom generator vs Arbitrary instance in QuickCheck在 QuickCheck 中使用自定义生成器与任意实例
【发布时间】:2012-11-13 10:55:30
【问题描述】:

这是一个简单的函数。它接受输入 Int 并返回 (Int, Int) 对的(可能为空)列表,其中输入 Int 是任何对的立方元素的总和。

cubeDecomposition :: Int -> [(Int, Int)]
cubeDecomposition n = [(x, y) | x <- [1..m], y <- [x..m], x^3 + y^3 == n] 
  where m = truncate $ fromIntegral n ** (1/3)

-- cubeDecomposition 1729
-- [(1,12),(9,10)]

我想测试一下上述是否属实的属性;如果我对每个元素进行立方体并对任何返回元组求和,那么我会得到我的输入:

import Control.Arrow 

cubedElementsSumToN :: Int -> Bool
cubedElementsSumToN n = all (== n) d
    where d = map (uncurry (+) . ((^3) *** (^3))) (cubeDecomposition n)

出于运行时的考虑,我想在使用 QuickCheck 进行测试时将输入 Ints 限制为一定的大小。我可以定义一个合适的类型和Arbitrary 实例:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import Test.QuickCheck

newtype SmallInt = SmallInt Int
    deriving (Show, Eq, Enum, Ord, Num, Real, Integral)

instance Arbitrary SmallInt where
    arbitrary = fmap SmallInt (choose (-10000000, 10000000))

然后我想我必须定义使用SmallInt 而不是Int 的函数和属性的版本:

cubeDecompositionQC :: SmallInt -> [(SmallInt, SmallInt)]
cubeDecompositionQC n = [(x, y) | x <- [1..m], y <- [x..m], x^3 + y^3 == n] 
  where m = truncate $ fromIntegral n ** (1/3)

cubedElementsSumToN' :: SmallInt -> Bool
cubedElementsSumToN' n = all (== n) d
    where d = map (uncurry (+) . ((^3) *** (^3))) (cubeDecompositionQC n)

-- cubeDecompositionQC 1729
-- [(SmallInt 1,SmallInt 12),(SmallInt 9,SmallInt 10)]

这工作正常,标准的 100 次测试按预期通过。但是当我真正需要的只是一个自定义生成器时,似乎没有必要定义一个新的类型、实例和函数。所以我尝试了这个:

smallInts :: Gen Int
smallInts = choose (-10000000, 10000000)

cubedElementsSumToN'' :: Int -> Property
cubedElementsSumToN'' n = forAll smallInts $ \m -> all (== n) (d m)
    where d =   map (uncurry (+) . ((^3) *** (^3)))
              . cubeDecomposition

现在,我最初运行了几次,一切正常,所有测试都通过了。但在随后的运行中,我观察到了失败。增加测试大小可靠地找到一个:

*** Failed! Falsifiable (after 674 tests and 1 shrink):  
0
8205379

由于存在 两个 缩小的输入 - 0 和 8205379 - 从 QuickCheck 返回,我在这里有点困惑,我直观地期望有一个。此外,这些输入按预期工作(至少在我的可显示属性上):

*Main> cubedElementsSumToN 0
True
*Main> cubedElementsSumToN 8205379
True

因此,使用我定义的自定义Gen 的属性显然存在问题。

我做错了什么?

【问题讨论】:

  • 现在对我来说似乎很明显nm 没有理由应该相等,因为我要求它们在cubedElementsSumToN'' 中。因此,只要输入一个产生非空列表的元素,该属性就会变成False。我可能会在几分钟内自己回答这个问题。
  • 您不需要新版本的函数来使用newtype 包装器。相反,只需对其进行模式匹配即可获得底层Int。例如,您可以写cubedElementsSumToN' (SmallInt n) = ...,而不是使用forAll

标签: haskell quickcheck


【解决方案1】:

我很快意识到我所写的属性显然是不正确的。这是使用原始cubedElementsSumToN 属性的正确方法:

quickCheck (forAll smallInts cubedElementsSumToN)

读起来很自然。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-10
    • 1970-01-01
    • 2018-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多