使用Haskell的QuickCheck生成特定长度的列表

时间:2011-10-08 00:04:49

标签: haskell quickcheck

-- 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运行时,它会放弃,因为它不会生成足够长的列表。

如何编写一个生成长度超过随机整数的列表的生成器?

3 个答案:

答案 0 :(得分:12)

哈马尔的答案完全适合这个问题。但为了回答所提出的确切问题,我忍不住调查了一下。我们使用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

这很简单:我们只是利用Gen的Monad实例。如果你运行quickCheck prop_bang,你会发现它开始花了很长时间,因为它开始测试荒谬的长列表。让我们限制列表的长度,使其更快一些。此外,现在listLongerThan仅生成一个完全x+1长的列表;让我们再混合一下,再次利用Gen的Monad实例

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

您可以在ghci中使用sample smallNumbersample (listLongerThan 3)来确保它生成正确的内容。

答案 1 :(得分: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来确保我们不会尝试索引到一个空列表。

答案 2 :(得分:4)

这有效:

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

然而,问题在于丢弃了 lot 的样本值。您可以使用Positive之类的东西来帮助确保索引有效。

如果您想要更复杂,可以使用更多newtype包装器来尝试生成足够长度的值(可能使用sized,或者一起生成列表和索引:生成列表,然后生成基于列表长度的索引。)