(i) È possibile utilizzare normale funtoriale composizione/monade di combinarli:
gen_comb :: Gen (Int, [Int])
gen_comb = (,) <$> gen_elem <*> gen_arr
(Control.Applicative.liftA2
e Control.Monad.liftM2
sono anche bene, ovviamente)
(ii) non utilizzare suchThat
per limitare un intervallo. Può essere terribilmente inefficiente, in quanto genera istanze casuali fino a quando la condizione non viene soddisfatta, scartando il resto. Invece, è possibile utilizzare elements :: [a] -> Gen a
:
gen_elem' :: Gen Int
gen_elem' = elements [0..100]
gen_arr' :: Gen [Int]
gen_arr' = listOf gen_elem'
gen_comb' :: Gen (Int, [Int])
gen_comb' = (,) <$> elements [0..100] <*> listOf (elements [0..100])
Aggiornamento: Come Zeta ha osservato di seguito, possiamo fare ancora meglio in questo caso, utilizzando choose (0,100)
(choose :: Random a => (a, a) -> Gen a
) al posto di elements [0..100]
. Vedere here o here per l'elenco completo dei combinatori di generatori.
*Main> sample gen_arr'
[78]
[2,27]
[12,39]
[92,22,40,6,18,19,25,13,95,99]
...
*Main> sample gen_comb'
(9,[23,3])
(11,[67,38,11,79])
(5,[96,69,68,81,75,14,59,68])
...
suchThat
vs. elements
:
*Main> sample (suchThat arbitrary (\i -> i >= 10000 && i <= 10005))
^CInterrupted.
*Main> sample (elements [10000..10005])
10003
10002
10000
10000
...
Il generatore suchThat
non ha uscita nulla.
Considerare 'choose (0,100)' invece di 'elements [0..100]'. Mentre 'elements' è ottimo se il tuo range non è continuo o se il tuo tipo non ha un'istanza' Random', 'choose' è solitamente più efficiente se i tuoi valori sono in un range chiuso. – Zeta