Skip to content

Commit 511cce8

Browse files
authored
Merge pull request #8 from matthewleon/Foldable1-gens
Generalize generators from NonEmpty to Foldable1
2 parents d9eeb27 + e4ad3e8 commit 511cce8

File tree

2 files changed

+57
-39
lines changed

2 files changed

+57
-39
lines changed

bower.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
"package.json"
1717
],
1818
"dependencies": {
19-
"purescript-nonempty": "^4.0.0",
2019
"purescript-tailrec": "^3.0.0",
2120
"purescript-tuples": "^4.0.0",
2221
"purescript-unfoldable": "^3.0.0",
23-
"purescript-integers": "^3.0.0"
22+
"purescript-integers": "^3.0.0",
23+
"purescript-foldable-traversable": "^3.3.0",
24+
"purescript-nonempty": "^4.2.0"
2425
},
2526
"devDependencies": {
2627
"purescript-console": "^3.0.0",

src/Control/Monad/Gen.purs

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ import Prelude
1212

1313
import Control.Monad.Gen.Class (class MonadGen, Size, chooseBool, chooseFloat, chooseInt, resize, sized)
1414
import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM)
15-
16-
import Data.Foldable (class Foldable, length, foldl, foldMap)
17-
import Data.Maybe (Maybe(..), fromMaybe)
15+
import Data.Foldable (foldMap, length)
16+
import Data.Maybe (Maybe(..))
1817
import Data.Monoid.Additive (Additive(..))
1918
import Data.Newtype (alaF)
20-
import Data.NonEmpty (NonEmpty, (:|))
19+
import Data.Semigroup.Foldable (class Foldable1, foldMap1)
2120
import Data.Tuple (Tuple(..), fst, snd)
2221
import Data.Unfoldable (class Unfoldable, unfoldr)
2322

@@ -30,10 +29,29 @@ choose genA genB = chooseBool >>= if _ then genA else genB
3029

3130
-- | Creates a generator that outputs a value chosen from a selection of
3231
-- | existing generators with uniform probability.
33-
oneOf :: forall m f a. MonadGen m => Foldable f => NonEmpty f (m a) -> m a
34-
oneOf (x :| xs) = do
35-
n <- chooseInt 0 (length xs)
36-
if n < 1 then x else fromIndex (n - 1) x xs
32+
oneOf :: forall m f a. MonadGen m => Foldable1 f => f (m a) -> m a
33+
oneOf xs = do
34+
n <- chooseInt 0 (length xs - 1)
35+
fromIndex n xs
36+
37+
newtype FreqSemigroup a = FreqSemigroup (Number -> Tuple (Maybe Number) a)
38+
39+
freqSemigroup :: forall a. Tuple Number a -> FreqSemigroup a
40+
freqSemigroup (Tuple weight x) =
41+
FreqSemigroup \pos ->
42+
if pos >= weight
43+
then Tuple (Just (pos - weight)) x
44+
else Tuple Nothing x
45+
46+
getFreqVal :: forall a. FreqSemigroup a -> Number -> a
47+
getFreqVal (FreqSemigroup f) = snd <<< f
48+
49+
instance semigroupFreqSemigroup :: Semigroup (FreqSemigroup a) where
50+
append (FreqSemigroup f) (FreqSemigroup g) =
51+
FreqSemigroup \pos ->
52+
case f pos of
53+
Tuple (Just pos') _ -> g pos'
54+
result -> result
3755

3856
-- | Creates a generator that outputs a value chosen from a selection of
3957
-- | existing generators, where the selection has weight values for the
@@ -42,35 +60,19 @@ oneOf (x :| xs) = do
4260
frequency
4361
:: forall m f a
4462
. MonadGen m
45-
=> Foldable f
46-
=> NonEmpty f (Tuple Number (m a))
63+
=> Foldable1 f
64+
=> f (Tuple Number (m a))
4765
-> m a
48-
frequency (x :| xs) =
49-
let
50-
first = fst x
51-
total = first + alaF Additive foldMap fst xs
52-
in
53-
chooseFloat 0.0 total >>= pick
54-
where
55-
pick pos =
56-
let
57-
initial = go (Tuple 0.0 $ snd x) x
58-
in
59-
snd $ foldl go initial xs
60-
where
61-
go (Tuple weight val) (Tuple currWeight currVal) =
62-
let
63-
nextWeight = weight + currWeight
64-
in
65-
if weight <= pos && pos <= nextWeight
66-
then Tuple nextWeight currVal
67-
else Tuple nextWeight val
66+
frequency xs =
67+
let total = alaF Additive foldMap fst xs
68+
in chooseFloat 0.0 total >>= getFreqVal (foldMap1 freqSemigroup xs)
69+
6870
-- | Creates a generator that outputs a value chosen from a selection with
6971
-- | uniform probability.
70-
elements :: forall m f a. MonadGen m => Foldable f => NonEmpty f a -> m a
71-
elements (x :| xs) = do
72-
n <- chooseInt 0 (length xs)
73-
pure if n == 0 then x else fromIndex (n - 1) x xs
72+
elements :: forall m f a. MonadGen m => Foldable1 f => f a -> m a
73+
elements xs = do
74+
n <- chooseInt 0 (length xs - 1)
75+
pure $ fromIndex n xs
7476

7577
-- | Creates a generator that produces unfoldable structures based on an
7678
-- | existing generator for the elements.
@@ -115,7 +117,22 @@ filtered gen = tailRecM go unit
115117
Nothing -> Loop unit
116118
Just a' -> Done a'
117119

118-
fromIndex :: forall f a. Foldable f => Int -> a -> f a -> a
119-
fromIndex i a = fromMaybe a <<< snd <<< (foldl go (Tuple 0 (Just a)))
120+
-- | Internal: used by fromIndex
121+
newtype AtIndex a = AtIndex (Int -> a)
122+
123+
instance semigroupAtIndex :: Semigroup (AtIndex a)
120124
where
121-
go (Tuple ix v) x = Tuple (ix + 1) (if ix == i then Just x else v)
125+
append (AtIndex f) (AtIndex g) =
126+
AtIndex \i -> if i <= 0 then f i else g (i - 1)
127+
128+
atIndex :: forall a. a -> AtIndex a
129+
atIndex = AtIndex <<< const
130+
131+
getAtIndex :: forall a. AtIndex a -> Int -> a
132+
getAtIndex (AtIndex f) = f
133+
134+
-- | Internal: get the Foldable element at index i.
135+
-- | If the index is <= 0, return the first element.
136+
-- | If it's >= length, return the last.
137+
fromIndex :: forall f a. Foldable1 f => Int -> f a -> a
138+
fromIndex i xs = getAtIndex (foldMap1 atIndex xs) i

0 commit comments

Comments
 (0)