Haskell QuickCheck为具有许多输入变量的函数生成随机数据

时间:2013-08-23 17:11:26

标签: haskell quickcheck

我有一个带有以下类型签名的函数

rndListIndex :: Double -> Double -> Double -> Double
rndListIndex maxIdx r1 r2 = …
  • 第一个输入应该是来自非负严格正整数的值
  • 第二个和第三个输入必须在关闭区间[0.0,1.0]内,否则功能没有意义

该函数具有

的属性
prop_alwaysLessThanMaxIdx idx r1 r2 = (rndListIndex idx r1 r2 <= idx)

如何分别为maxIdxr1r2生成随机数据;我知道函数choose但不知道如何将它与多个输入变量一起使用。

目前我已使用固定idx测试了该属性,这不是应该测试的方式。

2 个答案:

答案 0 :(得分:13)

您必须使用QuickCheck中的forAll功能。它有以下类型:

forAll :: (Show a, Testable prop) 
       => Gen a           -- ^ The generator to use for generating values
       -> (a -> prop)     -- ^ A function which returns a testable property
       -> Property                  

forAll有两个参数:

  • 生成器描述了如何生成值。生成器的示例包括choosearbitraryoneof,...
  • 该函数测试给定输入的属性。它必须返回一个Testable实例的值,例如另一个PropertyBool或函数。

使用choose和elements生成器嵌套forAll的示例:

-- This generates a Property p for all x's in the closed interval [1,3]
-- The property p in turn generates a property q for all y ∈ [4,5]
-- The property q is True if x < y.
prop_choose = forAll (choose (1,3)) $ \x ->
              forAll (elements [4,5]) $ \y -> x < y

对于您的测试属性,您可以使用forAll并选择第二个和第三个参数。 对于第一个参数,QuickCheck中有Positive a类型,可用于生成类型a的任意正值(当a为Num时,它具有任意实例):

prop_alwayLessThanMaxIdx :: Positive Integer -> Property
prop_alwaysLessThanMaxIdx (Positive idx) = 
  forAll (choose (0,1)) $ \r1 ->
  forAll (choose (0,1)) $ \r2 ->
   (rndListIndex idx r1 r2) < idx

答案 1 :(得分:1)

我建议您定义自己的包装Double的类型并为其指定一个Arbitrary实例,该实例仅生成0到1之间的数字。例如:

import Test.QuickCheck
newtype UnitInterval = Unit Double deriving Show

instance Arbitrary UnitInterval where
  arbitrary = fmap Unit (choose (0, 1))
  shrink (Unit x) = [ Unit y | y <- shrink x, 0 <= y && y <= 1 ]

要生成idx,您可以使用QuickCheck的Positive修饰符,如@bennoffs建议的那样(您必须导入Test.QuickCheck.Modifiers)。这类似于我在上面定义的UnitInterval类型,但生成正数而不是0到1之间的数字。您的属性将如下所示:

prop_alwaysLessThanMaxIdx (Positive idx) (Unit r1) (Unit r2) =
  rndListIndex idx r1 r2 <= idx