6

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

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

该函数具有

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

如何分别为 和 生成随机maxIdx数据;我知道该函数,但不知道如何将其与多个输入变量一起使用。r1r2choose

现在我已经用 fixed 测试了 Property idx,这不是它应该测试的方式。

4

2 回答 2

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接受两个参数:

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

带有选择和元素生成器的嵌套 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 与选择一起用于第二个和第三个参数。对于第一个参数,Positive aQuickCheck 中的类型可用于生成 a 类型的任意正值(当 a 为 Num 时,它有一个 Arbitrary 实例):

prop_alwayLessThanMaxIdx :: Positive Integer -> Property
prop_alwaysLessThanMaxIdx (Positive idx) = 
  forAll (choose (0,1)) $ \r1 ->
  forAll (choose (0,1)) $ \r2 ->
   (rndListIndex idx r1 r2) < idx
于 2013-08-23T18:09:28.163 回答
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
于 2013-08-26T20:57:51.933 回答