2012-11-13 61 views
4

這是一個簡單的函數。它需要一個輸入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我定義的屬性問題。

我做錯了什麼?

+0

現在看來很明顯,沒有理由'n'和'm'應該是相等的,因爲我要求它們在'cubedElementsSumToN'''中。所以,只要給它一個產生非空列表的元素,屬性就會變成「False」。我可能會在幾分鐘內回答這個問題。 – jtobin

+1

你不需要你的函數的新版本與newtype包裝器一起使用。相反,只需要模式匹配就可以得到底層的'Int'。例如,你可以寫'cubedElementsSumToN'(SmallInt n)= ...'而不是使用'forAll'。 – hammar

回答

2

我很快意識到,我寫的這個屬性顯然是不正確的。這裏是做正確的方式,利用原有cubedElementsSumToN屬性:

quickCheck (forAll smallInts cubedElementsSumToN) 

這很自然地讀。

相關問題