Skip to content

Commit b1bab02

Browse files
authored
Merge pull request #89 from purescript/iterator-groupby-2
Iterator groupby 2
2 parents 8f6dcc6 + 65e36a4 commit b1bab02

File tree

5 files changed

+156
-66
lines changed

5 files changed

+156
-66
lines changed

bower.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"purescript-st": "^2.0.0",
2323
"purescript-tailrec": "^2.0.0",
2424
"purescript-tuples": "^3.0.0",
25-
"purescript-unfoldable": "^2.0.0"
25+
"purescript-unfoldable": "^2.0.0",
26+
"purescript-unsafe-coerce": "^2.0.0"
2627
},
2728
"devDependencies": {
2829
"purescript-assert": "^2.0.0",

src/Data/Array.purs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,13 @@ module Data.Array
111111
) where
112112

113113
import Prelude
114-
115114
import Control.Alt ((<|>))
116115
import Control.Alternative (class Alternative)
117116
import Control.Lazy (class Lazy, defer)
118117
import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM2)
119-
118+
import Control.Monad.ST (pureST)
119+
import Data.Array.ST (unsafeFreeze, emptySTArray, pushSTArray)
120+
import Data.Array.ST.Iterator (iterator, iterate, pushWhile)
120121
import Data.Foldable (class Foldable, foldl, foldr)
121122
import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, elem, notElem, find, findMap, any, all) as Exports
122123
import Data.Maybe (Maybe(..), maybe, isJust, fromJust)
@@ -125,7 +126,6 @@ import Data.Traversable (scanl, scanr) as Exports
125126
import Data.Traversable (sequence, traverse)
126127
import Data.Tuple (Tuple(..))
127128
import Data.Unfoldable (class Unfoldable, unfoldr)
128-
129129
import Partial.Unsafe (unsafePartial)
130130

131131
-- | Convert an `Array` into an `Unfoldable` structure.
@@ -548,14 +548,16 @@ group' = group <<< sort
548548
-- | Group equal, consecutive elements of an array into arrays, using the
549549
-- | specified equivalence relation to detemine equality.
550550
groupBy :: forall a. (a -> a -> Boolean) -> Array a -> Array (NonEmpty Array a)
551-
groupBy op = go []
552-
where
553-
go :: Array (NonEmpty Array a) -> Array a -> Array (NonEmpty Array a)
554-
go acc xs = case uncons xs of
555-
Just o ->
556-
let sp = span (op o.head) o.tail
557-
in go ((o.head :| sp.init) : acc) sp.rest
558-
Nothing -> reverse acc
551+
groupBy op xs =
552+
pureST do
553+
result <- emptySTArray
554+
iter <- iterator (xs !! _)
555+
iterate iter \x -> void do
556+
sub <- emptySTArray
557+
pushWhile (op x) iter sub
558+
sub_ <- unsafeFreeze sub
559+
pushSTArray result (x :| sub_)
560+
unsafeFreeze result
559561

560562
-- | Remove the duplicates from an array, creating a new array.
561563
nub :: forall a. Eq a => Array a -> Array a

src/Data/Array/ST.purs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ module Data.Array.ST
1313
, pushAllSTArray
1414
, spliceSTArray
1515
, freeze, thaw
16+
, unsafeFreeze
1617
, toAssocArray
1718
) where
1819

20+
import Prelude
1921
import Control.Monad.Eff (Eff)
2022
import Control.Monad.ST (ST)
2123

2224
import Data.Maybe (Maybe(..))
25+
import Unsafe.Coerce (unsafeCoerce)
2326

2427
-- | A reference to a mutable array.
2528
-- |
@@ -33,6 +36,8 @@ foreign import data STArray :: * -> * -> *
3336
-- | An element and its index.
3437
type Assoc a = { value :: a, index :: Int }
3538

39+
-- | **DEPRECATED**: Use `unsafeFreeze` together with `runST` instead.
40+
-- |
3641
-- | Freeze a mutable array, creating an immutable array. Use this function as you would use
3742
-- | `runST` to freeze a mutable reference.
3843
-- |
@@ -42,6 +47,11 @@ foreign import runSTArray
4247
. (forall h. Eff (st :: ST h | r) (STArray h a))
4348
-> Eff r (Array a)
4449

50+
-- | O(1). Convert a mutable array to an immutable array, without copying. The mutable
51+
-- | array must not be mutated afterwards.
52+
unsafeFreeze :: forall a r h. STArray h a -> Eff (st :: ST h | r) (Array a)
53+
unsafeFreeze = pure <<< (unsafeCoerce :: STArray h a -> Array a)
54+
4555
-- | Create an empty mutable array.
4656
foreign import emptySTArray :: forall a h r. Eff (st :: ST h | r) (STArray h a)
4757

src/Data/Array/ST/Iterator.purs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
module Data.Array.ST.Iterator
2+
( Iterator
3+
, iterator
4+
, iterate
5+
, next
6+
, peek
7+
, exhausted
8+
, pushWhile
9+
, pushAll
10+
) where
11+
12+
import Prelude
13+
import Control.Monad.Eff (Eff, whileE)
14+
import Control.Monad.ST (ST, STRef, newSTRef, readSTRef, writeSTRef, modifySTRef)
15+
import Data.Array.ST (STArray, pushSTArray)
16+
17+
import Data.Maybe (Maybe(..), isNothing)
18+
19+
-- | This type provides a slightly easier way of iterating over an array's
20+
-- | elements in an STArray computation, without having to keep track of
21+
-- | indices.
22+
data Iterator h a = Iterator (Int -> Maybe a) (STRef h Int)
23+
24+
-- | Make an Iterator given an indexing function into an array (or anything
25+
-- | else). If `xs :: Array a`, the standard way to create an iterator over
26+
-- | `xs` is to use `iterator (xs !! _)`, where `(!!)` comes from `Data.Array`.
27+
iterator :: forall a h r. (Int -> Maybe a) -> Eff (st :: ST h | r) (Iterator h a)
28+
iterator f =
29+
Iterator f <$> newSTRef 0
30+
31+
-- | Perform an action once for each item left in an iterator. If the action
32+
-- | itself also advances the same iterator, `iterate` will miss those items
33+
-- | out.
34+
iterate :: forall a h r. Iterator h a -> (a -> Eff (st :: ST h | r) Unit) -> Eff (st :: ST h | r) Unit
35+
iterate iter f = do
36+
break <- newSTRef false
37+
whileE (not <$> readSTRef break) do
38+
mx <- next iter
39+
case mx of
40+
Just x -> f x
41+
Nothing -> void $ writeSTRef break true
42+
43+
-- | Get the next item out of an iterator, advancing it. Returns Nothing if the
44+
-- | Iterator is exhausted.
45+
next :: forall a h r. Iterator h a -> Eff (st :: ST h | r) (Maybe a)
46+
next (Iterator f currentIndex) = do
47+
i <- readSTRef currentIndex
48+
modifySTRef currentIndex (_ + 1)
49+
pure (f i)
50+
51+
-- | Get the next item out of an iterator without advancing it.
52+
peek :: forall a h r. Iterator h a -> Eff (st :: ST h | r) (Maybe a)
53+
peek (Iterator f currentIndex) = do
54+
i <- readSTRef currentIndex
55+
pure (f i)
56+
57+
-- | Check whether an iterator has been exhausted.
58+
exhausted :: forall a h r. Iterator h a -> Eff (st :: ST h | r) Boolean
59+
exhausted = map isNothing <<< peek
60+
61+
-- | Extract elements from an iterator and push them on to an STArray for as
62+
-- | long as those elements satisfy a given predicate.
63+
pushWhile :: forall a h r. (a -> Boolean) -> Iterator h a -> STArray h a -> Eff (st :: ST h | r) Unit
64+
pushWhile p iter array = do
65+
break <- newSTRef false
66+
whileE (not <$> readSTRef break) do
67+
mx <- peek iter
68+
case mx of
69+
Just x | p x -> do
70+
pushSTArray array x
71+
void $ next iter
72+
_ ->
73+
void $ writeSTRef break true
74+
75+
-- | Push the entire remaining contents of an iterator onto an STArray.
76+
pushAll :: forall a h r. Iterator h a -> STArray h a -> Eff (st :: ST h | r) Unit
77+
pushAll = pushWhile (const true)

test/Test/Data/Array/ST.purs

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,157 @@
11
module Test.Data.Array.ST (testArrayST) where
22

33
import Prelude
4-
5-
import Control.Monad.Eff (runPure, Eff)
4+
import Control.Monad.Eff (Eff)
65
import Control.Monad.Eff.Console (log, CONSOLE)
7-
import Control.Monad.ST (runST)
8-
9-
import Data.Array.ST (toAssocArray, thaw, spliceSTArray, runSTArray, pokeSTArray, emptySTArray, peekSTArray, pushAllSTArray, pushSTArray, freeze)
6+
import Control.Monad.ST (ST, pureST)
7+
import Data.Array.ST (STArray, emptySTArray, freeze, peekSTArray, pokeSTArray, pushAllSTArray, pushSTArray, spliceSTArray, thaw, toAssocArray, unsafeFreeze)
108
import Data.Foldable (all)
119
import Data.Maybe (Maybe(..), isNothing)
12-
1310
import Test.Assert (assert, ASSERT)
1411

12+
run :: forall a. (forall h. Eff (st :: ST h) (STArray h a)) -> Array a
13+
run act = pureST (act >>= unsafeFreeze)
14+
1515
testArrayST :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit
1616
testArrayST = do
1717

1818
log "emptySTArray should produce an empty array"
1919

20-
assert $ runPure (runSTArray emptySTArray) == nil
20+
assert $ run emptySTArray == nil
2121

2222
log "thaw should produce an STArray from a standard array"
2323

24-
assert $ runPure (runSTArray (thaw [1, 2, 3])) == [1, 2, 3]
24+
assert $ run (thaw [1, 2, 3]) == [1, 2, 3]
2525

2626
log "freeze should produce a standard array from an STArray"
2727

28-
assert $ runPure (runST (do
28+
assert $ pureST (do
2929
arr <- thaw [1, 2, 3]
30-
freeze arr)) == [1, 2, 3]
30+
freeze arr) == [1, 2, 3]
3131

3232
log "pushSTArray should append a value to the end of the array"
3333

34-
assert $ runPure (runSTArray (do
34+
assert $ run (do
3535
arr <- emptySTArray
3636
pushSTArray arr 1
3737
pushSTArray arr 2
38-
pure arr)) == [1, 2]
38+
pure arr) == [1, 2]
3939

40-
assert $ runPure (runSTArray (do
40+
assert $ run (do
4141
arr <- thaw [1, 2, 3]
4242
pushSTArray arr 4
43-
pure arr)) == [1, 2, 3, 4]
43+
pure arr) == [1, 2, 3, 4]
4444

4545
log "pushAllSTArray should append multiple values to the end of the array"
4646

47-
assert $ runPure (runSTArray (do
47+
assert $ run (do
4848
arr <- emptySTArray
4949
pushAllSTArray arr [1, 2]
50-
pure arr)) == [1, 2]
50+
pure arr) == [1, 2]
5151

52-
assert $ runPure (runSTArray (do
52+
assert $ run (do
5353
arr <- thaw [1, 2, 3]
5454
pushAllSTArray arr [4, 5, 6]
55-
pure arr)) == [1, 2, 3, 4, 5, 6]
55+
pure arr) == [1, 2, 3, 4, 5, 6]
5656

5757
log "peekSTArray should return Nothing when peeking a value outside the array bounds"
5858

59-
assert $ isNothing $ runPure (runST (do
59+
assert $ isNothing $ pureST (do
6060
arr <- emptySTArray
61-
peekSTArray arr 0))
61+
peekSTArray arr 0)
6262

63-
assert $ isNothing $ runPure (runST (do
63+
assert $ isNothing $ pureST (do
6464
arr <- thaw [1]
65-
peekSTArray arr 1))
65+
peekSTArray arr 1)
6666

67-
assert $ isNothing $ runPure (runST (do
67+
assert $ isNothing $ pureST (do
6868
arr <- emptySTArray
69-
peekSTArray arr (-1)))
69+
peekSTArray arr (-1))
7070

7171
log "peekSTArray should return the value at the specified index"
7272

73-
assert $ runPure (runST (do
73+
assert $ pureST (do
7474
arr <- thaw [1]
75-
peekSTArray arr 0)) == Just 1
75+
peekSTArray arr 0) == Just 1
7676

77-
assert $ runPure (runST (do
77+
assert $ pureST (do
7878
arr <- thaw [1, 2, 3]
79-
peekSTArray arr 2)) == Just 3
79+
peekSTArray arr 2) == Just 3
8080

8181
log "pokeSTArray should return true when a value has been updated succesfully"
8282

83-
assert $ runPure (runST (do
83+
assert $ pureST (do
8484
arr <- thaw [1]
85-
pokeSTArray arr 0 10))
85+
pokeSTArray arr 0 10)
8686

87-
assert $ runPure (runST (do
87+
assert $ pureST (do
8888
arr <- thaw [1, 2, 3]
89-
pokeSTArray arr 2 30))
89+
pokeSTArray arr 2 30)
9090

9191
log "pokeSTArray should return false when attempting to modify a value outside the array bounds"
9292

93-
assert $ not $ runPure (runST (do
93+
assert $ not $ pureST (do
9494
arr <- emptySTArray
95-
pokeSTArray arr 0 10))
95+
pokeSTArray arr 0 10)
9696

97-
assert $ not $ runPure (runST (do
97+
assert $ not $ pureST (do
9898
arr <- thaw [1, 2, 3]
99-
pokeSTArray arr 3 100))
99+
pokeSTArray arr 3 100)
100100

101-
assert $ not $ runPure (runST (do
101+
assert $ not $ pureST (do
102102
arr <- thaw [1, 2, 3]
103-
pokeSTArray arr (-1) 100))
103+
pokeSTArray arr (-1) 100)
104104

105105
log "pokeSTArray should replace the value at the specified index"
106106

107-
assert $ runPure (runSTArray (do
107+
assert $ run (do
108108
arr <- thaw [1]
109109
pokeSTArray arr 0 10
110-
pure arr)) == [10]
110+
pure arr) == [10]
111111

112112
log "pokeSTArray should do nothing when attempting to modify a value outside the array bounds"
113113

114-
assert $ runPure (runSTArray (do
114+
assert $ run (do
115115
arr <- thaw [1]
116116
pokeSTArray arr 1 2
117-
pure arr)) == [1]
117+
pure arr) == [1]
118118

119119
log "spliceSTArray should be able to delete multiple items at a specified index"
120120

121-
assert $ runPure (runSTArray (do
121+
assert $ run (do
122122
arr <- thaw [1, 2, 3, 4, 5]
123123
spliceSTArray arr 1 3 []
124-
pure arr)) == [1, 5]
124+
pure arr) == [1, 5]
125125

126126
log "spliceSTArray should return the items removed"
127127

128-
assert $ runPure (runST (do
128+
assert $ pureST (do
129129
arr <- thaw [1, 2, 3, 4, 5]
130-
spliceSTArray arr 1 3 [])) == [2, 3, 4]
130+
spliceSTArray arr 1 3 []) == [2, 3, 4]
131131

132132
log "spliceSTArray should be able to insert multiple items at a specified index"
133133

134-
assert $ runPure (runSTArray (do
134+
assert $ run (do
135135
arr <- thaw [1, 2, 3, 4, 5]
136136
spliceSTArray arr 1 0 [0, 100]
137-
pure arr)) == [1, 0, 100, 2, 3, 4, 5]
137+
pure arr) == [1, 0, 100, 2, 3, 4, 5]
138138

139139
log "spliceSTArray should be able to delete and insert at the same time"
140140

141-
assert $ runPure (runSTArray (do
141+
assert $ run (do
142142
arr <- thaw [1, 2, 3, 4, 5]
143143
spliceSTArray arr 1 2 [0, 100]
144-
pure arr)) == [1, 0, 100, 4, 5]
144+
pure arr) == [1, 0, 100, 4, 5]
145145

146146
log "toAssocArray should return all items in the array with the correct indices and values"
147147

148-
assert $ all (\{ value: v, index: i } -> v == i + 1) $ runPure (runST (do
148+
assert $ all (\{ value: v, index: i } -> v == i + 1) $ pureST (do
149149
arr <- thaw [1, 2, 3, 4, 5]
150-
toAssocArray arr))
150+
toAssocArray arr)
151151

152-
assert $ all (\{ value: v, index: i } -> v == (i + 1) * 10) $ runPure (runST (do
152+
assert $ all (\{ value: v, index: i } -> v == (i + 1) * 10) $ pureST (do
153153
arr <- thaw [10, 20, 30, 40, 50]
154-
toAssocArray arr))
154+
toAssocArray arr)
155155

156156
nil :: Array Int
157157
nil = []

0 commit comments

Comments
 (0)