2011-10-08 72 views
12
-- 3 (find k"th element of a list) 
element_at xs x = xs !! x 
prop_3a xs x = (x < length xs && x >= 0) ==> element_at xs (x::Int) == (xs !! x::Int) 

當prop_3a運行QuickCheck時,它會放棄,因爲它不會生成足夠長的列表。通過Haskell的QuickCheck生成特定長度的列表

如何編寫一個將生成長度大於隨機整數的列表的生成器?

回答

10

去其他方式怎麼樣?首先,我們讓QuickCheck挑選一個列表,然後限制我們允許的索引。這是有效的,並且不會丟棄任何測試用例。

prop_3a (NonEmpty xs) = forAll (choose (0, length xs - 1)) $ \i -> 
    element_at xs i == (xs !! i :: Int) 

在這裏,我用forAll使用特定的發電機的指數,在這種情況下使用choose從指定的範圍內挑選一個元素,我也用the NonEmptyList type以確保我們不要試圖指數進入一個空的列表。

+0

這似乎很好地工作,但我需要更多地研究代碼來理解它。 :) –

+0

除了使用谷歌搜索,我怎麼能找出哪些軟件包提供NonEmpty? –

+1

@JoeVanDyk:這是[來自QuickCheck](http://hackage.haskell.org/packages/archive/QuickCheck/2.4.1.1/doc/html/Test-QuickCheck.html#t:NonEmptyList)。 – hammar

3

這工作:

import Test.QuickCheck 

element_at  :: [a] -> Int -> a 
element_at xs i = xs !! i 

prop_3a  :: [Int] -> Int -> Property 
prop_3a xs i = (i >= 0) ==> (length xs > i) ==> element_at xs i == xs !! i 

然而,這個問題是一個樣本值的很多被丟棄。你可以使用像Positive這樣的東西來幫助確保索引是有效的。

如果想要更復雜一點,可以使用更多的newtype包裝來嘗試生成足夠長度的值(可能使用sized或者一起生成列表和索引:生成列表,然後生成基於索引的根據列表的長度)。

9

hammar的回答完全可以解決問題。但爲了回答所問的確切問題,我忍不住調查了一下。我們使用forAll

prop_bang x = x >= 0 ==> forAll (listLongerThan x) $ \xs -> 
    element_at xs x == xs !! x 

所以現在我們需要一個函數,listLongerThan :: Int -> Gen [Int]。它需要一個長度x,並生成一個生成器,它將生成長度大於x的列表。

listLongerThan :: Int -> Gen [Int] 
listLongerThan x = replicateM (x+1) arbitrary 

這很簡單:我們只是利用Monad實例Gen。如果你運行quickCheck prop_bang,你會注意到它開始需要相當長的時間,因爲它開始測試荒謬的長列表。讓我們限制列表的長度,讓它變得更快一點。此外,現在listLongerThan只生成一個確切長度爲x+1的列表;讓我們這混合了一下,再利用創

prop_bang = 
    forAll smallNumber $ \x -> 
    forAll (listLongerThan x) $ \xs -> 
    element_at xs x == xs !! x 

smallNumber :: Gen Int 
smallNumber = fmap ((`mod` 100) . abs) arbitrary 

listLongerThan :: Int -> Gen [Int] 
listLongerThan x = do 
    y <- fmap (+1) smallNumber -- y > 0 
    replicateM (x+y) arbitrary 
單子實例可以使用 sample smallNumbersample (listLongerThan 3)在ghci中,以確保它產生正確的東西。

+2

而不是replicateM,我寧願使用'vector'函數 –