Domanda

I have a function with the following type signature

rndListIndex :: Double -> Double -> Double -> Double
rndListIndex maxIdx r1 r2 = …
  • the first input should be a value coming from a non-negative strictly positive integer
  • the second and third input are required to be within the closed interval [0.0,1.0] otherwise the function makes no sense

the function has the property of

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

how do I generate random data for maxIdx and r1,r2 separately; I know of the function choosebut do not know how to use it with more than one input variable.

For now I have tested the Property with fixed idx, which is not the way it should be tested.

È stato utile?

Soluzione

You have to use the forAll function from QuickCheck. It has the following type:

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 takes two arguments:

  • The generator describes how to generate values. Examples of generators are choose, arbitrary, oneof, ...
  • The function tests the property for the given input. It must return a value that is an instance of Testable, for example another Property, Bool or a function.

Example of a nested forAll with the choose and elements generators:

-- 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

For your test property, you can use forAll with choose for the second and third argument. For the first argument, there is the Positive a type in QuickCheck which can be used to generate arbitrary positive values of type a (It has an Arbitrary instance when a is 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

Altri suggerimenti

I would suggest defining your own type that wraps Double and give it an Arbitrary instance that only generates numbers between 0 and 1. Something like:

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 ]

For generating idx, you can use QuickCheck's Positive modifier, as @bennoffs suggested (you will have to import Test.QuickCheck.Modifiers). This is similar to the UnitInterval type I defined above, but generates positive numbers instead of numbers between 0 and 1. Your property will then look like:

prop_alwaysLessThanMaxIdx (Positive idx) (Unit r1) (Unit r2) =
  rndListIndex idx r1 r2 <= idx
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top