简体   繁体   English

使用类型同义词时如何约束QuickCheck?

[英]How do I constrain QuickCheck when using type synonyms?

I am using QuickCheck to run arbitrary test cases on my code. 我正在使用QuickCheck在我的代码上运行任意测试用例。 However, in one portion of my code I have the type synonym: 但是,在我的代码的一部分中,我具有类型同义词:

type Vector = [Double]

I also have a few functions that accept a number of Vector s as input. 我也有一些函数可以接受许多Vector作为输入。 However, all of these functions require that the Vector s be of the same length. 但是,所有这些功能都要求Vector的长度相同。

Is there a way to constrain QuickCheck so that it only generates lists of length n ? 有没有一种方法可以限制QuickCheck ,使其仅生成长度为n的列表?

A simple solution is to not have an arbitrary instance but instead to do something like 一个简单的解决方案是不具有任意实例,而要做类似的事情

import Test.QuickCheck
import Control.Monad

prop_vec :: Int -> Gen [Double]
prop_vec = flip replicateM arbitrary . abs


prop_addComm :: Int -> Gen Bool
prop_addComm i  = do
  v <- prop_vec i
  u <- prop_vec i
  return $ u + v = v + u --assuming you'd added a Num instance for your vectors

There's never a typeclass so you get less helpful failures, but it's simpler to whip up. 从来没有类型类,所以您得到的帮助失败较少,但是更容易上手。

You can set constraints with the ==> notation. 您可以使用==>表示法设置约束。

an example is: 一个例子是:

prop_test xs = minimum xs == (head $ sort xs)

which fails: 失败:

*** Failed! Exception: 'Prelude.minimum: empty list' (after 1 test):
[]

now with a constraint: 现在有一个约束:

prop_test xs = not (null xs) ==> minimum xs == (head $ sort xs)

it works: 有用:

*Main> quickCheck prop_test
+++ OK, passed 100 tests.

in your case: 在您的情况下:

prop_test xs ys = length xs == length ys ==> undefined -- whatever you want

The other obvious solution is to generate a list of tuples and unzip them. 另一个明显的解决方案是生成一个元组列表并将其解压缩。 For example, in ghci: 例如,在ghci中:

> let allSameLength (xs:xss) = all (==length xs) (map length xss)
> quickCheck (\xys -> let (xs, ys) = unzip xys in allSameLength [xs, ys])
+++ OK, passed 100 tests.
> :{
| quickCheck (\wxyzs -> let
|   (wxs, yzs) = unzip wxyzs
|   (ws, xs) = unzip wxs
|   (ys, zs) = unzip yzs
|   in allSameLength [ws, xs, ys, zs])
| :}
+++ OK, passed 100 tests.

Here's one possibility. 这是一种可能性。 We'll define a new class for types that can build a size-dependent random value. 我们将为可以构建与大小相关的随机值的类型定义一个新类。 Then you can make a type-level list or tree or whatever and declare one Arbitrary instance for these once and for all. 然后,您可以创建类型级别的列表或树或其他任何内容,并为这些声明一次Arbitrary实例。

import Control.Monad
import Test.QuickCheck

class SizedArbitrary a where
    sizedArbitrary :: Int -> Gen a

instance Arbitrary a => SizedArbitrary [a] where
    sizedArbitrary n = replicateM n arbitrary

data Branch a b = a :+ b deriving (Eq, Ord, Show, Read)
instance (SizedArbitrary a, SizedArbitrary b) => SizedArbitrary (Branch a b) where
    sizedArbitrary n = liftM2 (:+) (sizedArbitrary n) (sizedArbitrary n)

instance (SizedArbitrary a, SizedArbitrary b) => Arbitrary (Branch a b) where
    arbitrary = arbitrarySizedIntegral >>= sizedArbitrary . abs

Then we can load it up in ghci and check out that it works: 然后我们可以将其加载到ghci中,并检查其是否有效:

*Main> let allSameLength (xs:xss) = all (==length xs) (map length xss)
*Main> quickCheck (\(xs :+ ys) -> allSameLength [xs, ys])
+++ OK, passed 100 tests.
*Main> quickCheck (\(ws :+ xs :+ ys :+ zs) -> allSameLength [ws, xs, ys, zs])
+++ OK, passed 100 tests.

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

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