2016-08-17 2 views
0

Betrachten Sie das Problem der Erzeugung von Strings aus einer Reihe von möglichen Strings, so dass einmal eine Zeichenfolge ausgewählt wird, kann es nicht erneut wiederholt werden. Für diese Aufgabe möchte ich QuickCheck 's Gen Funktionen verwenden.Erzeugen von zufälligen Strings aus einem String-Pool mit QuickCheck

Wenn ich auf den Typ der Funktion schaue, die ich versuche zu schreiben, sieht es ziemlich wie eine Zustands-Monade aus. Da ich eine andere Monade verwende, nämlich Gen, innerhalb der State Monade. Ich schrieb meinen ersten Versuch mit StateT.

arbitraryStringS :: StateT GenState Gen String 
arbitraryStringS = 
    mapStateT stringGenS get 

wo:

newtype GenState = St {getStrings :: [String]} 
    deriving (Show) 

removeString :: String -> GenState -> GenState 
removeString str (St xs) = St $ delete str xs 

stringGenS :: Gen (a, GenState) -> Gen (String, GenState) 
stringGenS genStSt = 
    genStSt >>= \(_, st) -> 
    elements (getStrings st) >>= \str -> 
    return (str, removeString str st) 

Etwas, das mich an dieser Implementierung die Tatsache ist beunruhigt, dass ich nicht das erste Element stringGenS verwenden. Zweitens ist mein Endziel, einen Zufallsgenerator für JSON-Werte zu definieren, die einen Ressourcenpool verwenden (der nicht nur Strings enthält). Mit StateT führte mich „Stateful“ Varianten von QuickCheck ‚s elements, listOf usw.

Ich habe mich gefragt, zu implementieren, ob es einen besseren Weg, dies zu erreichen, oder eine solche Komplexität ist inhärent definieren Stateful Varianten bestehender Monaden.

+0

Ich würde es anders machen - um die erzeugten 'Strings' zu speichern - oder zumindest die Seeds und vergleichen jeden Seed/generierten String für die Mitgliedschaft in einem' Set' von Seeds/'String'. – epsilonhalbe

+0

eine andere Wahl könnte UUIDs verwenden, um "höchstwahrscheinlich" eindeutige Strings zu erzeugen, wenn Sie nur eine endliche Menge von Strings haben - Sie haben schließlich keine Strings mehr, können Sie mit großen Sets arbeiten, aber trotzdem werden Sie laufen in doppelte Strings - wenn Sie "echte Einzigartigkeit" brauchen, würde ich mit einem Basissatz + einem unendlichen Satz wie die natürlichen Zahlen gehen und diesen kombinieren. – epsilonhalbe

+0

Es ist wichtig, dass die Zeichenfolgen aus dem Ressourcenpool stammen. Dies kann verwendet werden, um Tests unter Verwendung von Daten zu erzeugen, die in einer Datenbank vorhanden sind. –

Antwort

1

Die Kombination aus StateT und Gen könnte wie folgt aussehen:

import Control.Monad.State 
import Data.List (delete) 
import Test.QuickCheck 

-- A more efficient solution would be to use Data.Set. 
-- Even better, Data.Trie and ByteStrings: 
-- https://hackage.haskell.org/package/bytestring-trie-0.2.4.1/docs/Data-Trie.html 
newtype GenState = St { getStrings :: [String] } 
    deriving (Show) 

removeString :: String -> GenState -> GenState 
removeString str (St xs) = St $ delete str xs 

stringGenS :: StateT GenState Gen String 
stringGenS = do 
    s <- get 
    str <- lift $ elements (getStrings s) 
    modify $ removeString str 
    return str 

Das Problem ist, dass, wie Sie den Zustand benötigen, können Sie nicht mehr solche Berechnungen in Gen beim Teil des Zustandes laufen können. Die einzige vernünftige Sache zu tun wäre, um gemeinsam mehrere zufällige eindeutige Zeichenfolgen zu erzeugen (den gleichen Zustand verwendet wird) als

evalStateT (replicateM 10 stringGenS) 

die GenState -> Gen [String] vom Typ ist.

+0

Danke. Die Verwendung von 'Lift' ist definitiv eleganter. In Bezug auf den Staat, wie ich in meinem vorherigen Kommentar erwähnte, da ich Daten generieren muss, die eine hierarchische Beziehung haben, begrenzt die Wahl eines Elements meine zukünftigen Entscheidungen, und das ist der Grund, warum ich den Zustand von einem Generator zu übergeben möchte das andere. Die Erzeugung von Strings war nur illustrativ. –

+0

Beachten Sie, dass 'Gen' Ihnen keine bestimmte Verteilung gibt. Wie es zum Lesen verwendet wird, versucht es, verschiedene Eckenfälle zu fangen, anstatt in irgendeiner Weise einheitlich zu sein. –

+0

Guter Punkt. Ich muss das definitiv in Zukunft berücksichtigen. Es könnte sein, dass ich am Ende eine "Random" -Monade benutze. Ich hoffe jedoch, dass ich die gleiche Technik der Monade-Transformatoren verwenden kann. –

Verwandte Themen