2014-11-27 7 views
6

Sono nuovo di Haskell. È molto bello finora, ma sto eseguendo il copia-incolla delle mie proprietà QuickCheck, e vorrei correggerlo.Come posso limitare i parametri QuickCheck, ad es. usare solo valori non negativi?

Ecco un esempio inventato:

prop_Myfunc :: [Int] -> (Int,Int) -> Bool 
prop_Myfunc ints (i,j) = ints !! i == ints !! j 

Questo non funzionerà perché QuickCheck genera numeri negativi, così ho ottenere

*** Failed! (after 2 tests and 2 shrinks):        
Exception: 
    Prelude.(!!): negative index 

ho cercato di google per soluzioni a questo, e ho trovato eg NonNegative e ==>, ma non capisco come funzionano.

Come posso limitare l'esempio precedente in modo che i e j non siano mai negativi? E anche, così che nessuno dei due è troppo alto? Cioè: 0 <= i,j < length ints

+0

Penso che intendiate '0 <= i, j ErikR

+0

@ user5402 Sì, grazie! Lo cambierò. Controllerà la tua risposta il prima possibile, sembra molto buona. –

risposta

5

involucri di vincoli (da Test.QuickCheck.Modifiers, se non sono riesportati implicitamente) può essere utilizzato in questo modo:

prop_Myfunc :: [Int] -> (NonNegative Int, NonNegative Int) -> Bool 
prop_Myfunc ints (NonNegative i, NonNegative j) = ints !! i == ints !! j 

Si può trattare SomeWrapper a come a con la distribuzione modificata. Ad esempio, NonNegative a assicura che a >= 0. Dopo che il wrapper è stato generato, il valore può essere ottenuto con pattern matching o accesso esplicito (getNonNegative in questo caso).

Per quanto riguarda il vincolo del margine superiore degli indici, penso che non sia possibile con i wrapper (nel sistema di tipi Haskkell è impossibile parametrizzare un tipo con il valore, in questo caso la lunghezza dell'elenco). Tuttavia, con l'operatore ==> è possibile aggiungere un vincolo booleano arbitrario per il test:

prop_Myfunc ints (NonNegative i, NonNegative j) = i < l && j < l ==> ints !! i == ints !! j where 
    l = length ints 

Funziona in altro modo: quando la condizione non è vera, si scarta semplicemente il caso di test corrente. Ma attenzione: se ci sono troppi casi lanciati (la condizione è troppo restrittiva), il test diventa molto meno utile. Un comportamento "senza perdita" può essere spesso ottenuto con i dati di test shrink, ma è un argomento completamente diverso.

+0

Grazie! Questo è esattamente ciò di cui avevo bisogno. –

10

Innanzitutto, vedere this SO answer per un esempio di come scrivere una funzione personalizzata Gen ... e come utilizzare il combinatore forAll.

e qui è come scrivere un generatore per una lista non vuota e due indici non negativi validi in lista:

import Test.QuickCheck 

genArgs :: Gen ([Int], Int, Int) 
genArgs = do 
    x <- arbitrary 
    xs <- arbitrary 
    let n = length xs 
    i <- choose (0,n) 
    j <- choose (0,n) 
    return ((x:xs), i, j) -- return a non-empty list 

test = quickCheck $ forAll genArgs $ \(xs,i,j) -> prop_myfunc xs (i,j) 
+0

Grazie mille. Ho imparato molto da questo. La risposta di Yuuri era più in linea con quello che volevo, ma vorrei poterlo accettare. Avere un upvote, almeno. –

+1

In effetti, questa soluzione mi piace più della mia :) – Yuuri

0

ero in una situazione simile come voi e ho finalmente trovato il modo di utilizzare ==> qui: http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html, nella sezione "Proprietà condizionali".

Con il vostro esempio, che avrebbe dovuto sostituire il Bool con Property e inserire i requisiti circa le variabili prima della prova di proprietà, come segue:

prop_Myfunc :: [Int] -> (Int,Int) -> Property 
prop_Myfunc ints (i,j) = (i >= 0 && j >= 0) ==> ints !! i == ints !! j 

(non ho ancora testato su questo particolare esempio, ma ha funzionato per me su un caso simile.)

Nota il tipo di ==>: (==>) :: Testable prop => Bool -> prop -> Property.

Problemi correlati