2014-11-27 1 views
6

Ich bin neu in Haskell. Bis jetzt ist es sehr schön, aber ich stoße auf Kopieren-Einfügen für meine QuickCheck-Eigenschaften, und ich möchte das beheben.Wie kann ich Constraint QuickCheck Parameter, z. nur nicht negative ints verwenden?

Hier ist ein erfundenes Beispiel:

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

Das wird nicht funktionieren, weil Quick Check negative Zahlen erzeugt, so dass ich

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

Ich habe versucht, nach Lösungen für diese Google, und ich habe zB gefunden NonNegative und ==>, aber ich verstehe nicht, wie sie funktionieren.

Wie kann ich das obige Beispiel einschränken, so dass i und j niemals negativ sind? Und auch, damit keiner zu hoch ist? Das heißt: 0 <= i,j < length ints

+0

Ich denke du meinst '0 <= i, j ErikR

+0

@ user5402 Ja, danke! Ich werde das ändern. Werde deine Antwort so schnell wie möglich überprüfen, es sieht sehr gut aus. –

Antwort

5

Einschränkende Wrapper (von Test.QuickCheck.Modifiers, wenn sie nicht implizit wieder ausgeführt werden) können auf diese Art und Weise verwendet werden:

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

Sie SomeWrapper a als a mit modifizierten Verteilung behandeln kann. Zum Beispiel stellt NonNegative a sicher, dass a >= 0. Nachdem der Wrapper generiert wurde, kann der Wert mit pattern-matching oder explizitem Accessor (getNonNegative in diesem Fall) abgerufen werden.

Was den oberen Rand Ihrer Indizes betrifft, denke ich, dass es mit Wrappern nicht möglich ist (es ist im Haskkell-Typsystem unmöglich, einen Typ mit dem Wert, in diesem Fall die Listenlänge, zu parametrisieren). Doch mit dem ==> Operator können Sie eine beliebige boolean Einschränkung für Ihren Test hinzufügen:

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

in anderer Art und Weise funktioniert: Wenn die Bedingung nicht erfüllt ist, ist es einfach verwirft den aktuellen Testfall. Aber Vorsicht: Wenn es zu viele geworfene Fälle gibt (der Zustand ist zu restriktiv), wird der Test viel weniger nützlich. Ein "verlustfreies" Verhalten kann oft mit shrink Testdaten erreicht werden, aber es ist ein ganz anderes Thema.

+0

Danke! Genau das habe ich gebraucht. –

10

Zuerst finden Sie unter this SO answer für ein Beispiel zum Schreiben einer benutzerdefinierten Gen ... Funktion und wie Sie den forAll Kombinator verwenden.

Und hier ist, wie man einen Generator für eine nicht-leere Liste und zwei gültige Nicht-negative Indizes in die Liste schreiben:

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

Vielen Dank. Ich habe viel daraus gelernt. Yuuris Antwort entsprach mehr meinem Wunsch, aber ich wünschte, ich könnte es akzeptieren. Habe wenigstens eine Verbesserung. –

+1

Tatsächlich mag ich diese Lösung mehr als meine eigene :) – Yuuri

0

ich in einer ähnlichen Situation war, wie Sie und ich endlich gefunden, wie man Verwenden Sie ==> hier: http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html, im Abschnitt "Conditional Properties".

Mit Ihrem Beispiel, würden Sie Bool mit Property und legen Sie die Anforderungen über Ihre Variablen vor dem Eigenschaftstest ersetzen müssen, wie folgt:

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

(Ich habe nicht auf diesem speziellen Beispiel getestet, aber es funktionierte für mich in einem ähnlichen Fall.)

Beachte den Typ ==>: (==>) :: Testable prop => Bool -> prop -> Property.