這是一個簡單的函數。它需要一個輸入Int
並返回(可能爲空)(Int, Int)
對的列表,其中輸入Int
是任何對的立方元素的總和。在QuickCheck中使用自定義生成器vs任意實例
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)
對於運行時間的考慮,我想Int
•限制輸入到一定的規模與快速檢查測試這個時。我可以定義一個適當類型和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 - 從快速檢查,在那裏我會直覺地想到一回。此外,這些輸入工作所預測的(在我的節目,能夠物業,至少):
*Main> cubedElementsSumToN 0
True
*Main> cubedElementsSumToN 8205379
True
因此它似乎很明顯有一個在使用自定義Gen
我定義的屬性問題。
我做錯了什麼?
現在看來很明顯,沒有理由'n'和'm'應該是相等的,因爲我要求它們在'cubedElementsSumToN'''中。所以,只要給它一個產生非空列表的元素,屬性就會變成「False」。我可能會在幾分鐘內回答這個問題。 – jtobin
你不需要你的函數的新版本與newtype包裝器一起使用。相反,只需要模式匹配就可以得到底層的'Int'。例如,你可以寫'cubedElementsSumToN'(SmallInt n)= ...'而不是使用'forAll'。 – hammar