2011-10-08 50 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'函数 –