2016-08-17 2 views
0

Considérez le problème de génération de chaînes de sorte que, une fois qu'une chaîne est choisie, elle ne puisse plus être répétée. Pour cette tâche, je voudrais utiliser QuickCheckGen fonctions.Génération de chaînes aléatoires à partir d'un pool de chaînes à l'aide de QuickCheck

Si je regarde le type de la fonction que j'essaie d'écrire, cela ressemble beaucoup à une monade d'état. Depuis que j'utilise une autre monade, à savoir Gen, à l'intérieur de la monade d'état. J'ai écrit ma première tentative en utilisant StateT.

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

où:

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) 

Quelque chose qui me préoccupe au sujet de cette mise en œuvre est le fait que je ne suis pas en utilisant le premier élément de stringGenS. Deuxièmement, mon objectif final est de définir un générateur aléatoire pour les valeurs JSON, qui utilisent un pool de ressources (qui ne contient pas seulement des chaînes). L'utilisation StateT m'a conduit à mettre en œuvre, je me demandais « stateful » variantes de l » elementsQuickCheck, listOf, etc.

s'il y a une meilleure façon d'y parvenir, ou une telle complexité est inhérente à la définition des variantes stateful de monades existants.

+0

Je le ferais dans l'autre sens - pour stocker les 'Strings' créées - ou au moins les graines et comparer chaque graine/chaîne générée pour l'appartenance à un' Set' de seeds/'String'. – epsilonhalbe

+0

un autre choix pourrait utiliser uuid pour générer "très probablement" des chaînes uniques, si vous avez seulement un ensemble fini de chaînes - vous finirez par manquer de chaînes, vous pouvez contourner en faisant des combinaisons de grands ensembles de base - mais vous continuerez dans les chaînes dupliquées - si vous avez besoin de "l'unicité réelle" j'irais avec un ensemble de base + un ensemble infini comme les nombres naturels et combiner cela. – epsilonhalbe

+0

Il est important que les chaînes proviennent du pool de ressources. Cela peut être utilisé pour générer des tests en utilisant des données existant dans certaines bases de données. –

Répondre

1

La combinaison de StateT et Gen pourrait ressembler à ceci:

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 

Le problème est que vous avez besoin de l'état, vous ne pouvez pas exécuter plusieurs calculs dans ces Gen tout en partageant l'état. La seule chose raisonnable à faire serait de générer plusieurs chaînes aléatoires uniques ensemble (en utilisant le même état) que

evalStateT (replicateM 10 stringGenS) 

qui est de type GenState -> Gen [String].

+0

Merci. L'utilisation de «lift» est définitivement plus élégante. En ce qui concerne l'État, comme je l'ai mentionné dans mon précédent commentaire, puisque j'ai besoin de générer des données hiérarchiques, le choix d'un élément limite mes choix futurs, et c'est la raison pour laquelle je souhaite passer de l'état d'un générateur à L'autre. La génération de chaînes était juste illustrative. –

+0

Attention, 'Gen' ne vous donne aucune distribution particulière. Comme il est utilisé pour la lecture, il essaie d'attraper différents cas de coin, plutôt que d'être uniforme de quelque façon que ce soit. –

+0

Bon point. Je dois absolument envisager cela à l'avenir. Il se pourrait que je finisse par utiliser une monade 'Random'. Cependant j'espère pouvoir utiliser la même technique de composition des transformateurs monad. –