简体   繁体   English

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

[英]Generating a lists of a specific length with Haskell's 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)

When prop_3a is ran through QuickCheck, it gives up, because it won't generate long enough lists. 当prop_3a通过QuickCheck运行时,它会放弃,因为它不会生成足够长的列表。

How can I write a generator that will generate lists with length longer than the random integer? 如何编写生成长度超过随机整数的列表的生成器?

hammar's answer is perfectly adequate for the problem. 哈马尔的答案完全适合这个问题。 But for the sake of answering the precise question asked, I couldn't help but investigate a bit. 但为了回答所提出的确切问题,我忍不住调查了一下。 Let's use forAll . 让我们一起使用forAll

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

So now we need a function, listLongerThan :: Int -> Gen [Int] . 所以现在我们需要一个函数listLongerThan :: Int -> Gen [Int] It takes a length, x, and produces a generator which will produce lists of length greater than x . 它需要一个长度x,并产生一个生成器,它将生成长度大于x列表。

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

It's rather straightforward: we simply take advantage of the Monad instance of Gen . 这很简单:我们只是利用Gen的Monad实例。 If you run quickCheck prop_bang , you'll notice it starts taking quite a long time, because it begins testing absurdly long lists. 如果你运行quickCheck prop_bang ,你会发现它开始花了很长时间,因为它开始测试荒谬的长列表。 Let's limit the length of the list, to make it go a bit faster. 让我们限制列表的长度,使其更快一些。 Also, right now listLongerThan only generates a list that is exactly x+1 long; 此外,现在listLongerThan只生成一个x+1长的列表; let's mix that up a bit, again utilizing the Monad instance of Gen. 让我们再混合一下,再次利用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

You can use sample smallNumber or sample (listLongerThan 3) in ghci to make sure it is generating the correct stuff. 您可以在ghci中使用sample smallNumbersample (listLongerThan 3)来确保它生成正确的内容。

How about going the other way? 走另一条路怎么样? First we let QuickCheck pick a list and then we constrain what indices we allow. 首先,我们让QuickCheck选择一个列表,然后我们约束我们允许的索引。 This works, and does not throw away any test cases. 这有效,并且不会丢弃任何测试用例。

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

Here, I use forAll to use a specific generator for the indices, in this case using choose which picks an element from a specified range, and I also use the NonEmptyList type to ensure that we don't try to index into an empty list. 在这里,我使用forAll为索引使用特定的生成器,在这种情况下使用choose从指定范围中选择一个元素,并且我还使用NonEmptyList类型来确保我们不尝试索引到空列表。

This works: 这有效:

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

However, the problem with this is that a lot of sample values are discarded. 但是,问题在于丢弃了大量样本值。 You could use things like Positive to help with ensuring that the index is valid. 您可以使用Positive东西来帮助确保索引有效。

If you want to be more complex, you can use more newtype wrappers to try and generate values of sufficient length (possibly using sized , or generate the list and the index together: generate the list, and then generate the index based upon the length of the list). 如果你想变得更复杂,你可以使用更多newtype包装器来尝试生成足够长度的值(可能使用sized ,或者一起生成列表和索引:生成列表,然后根据长度生成索引名单)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM