Skip to content

Commit 60fe96a

Browse files
committed
Faster groupBy
Implement `groupBy` using `Data.Array.ST.Iterator`; this should make it faster.
1 parent 76ec2d4 commit 60fe96a

File tree

3 files changed

+26
-9
lines changed

3 files changed

+26
-9
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: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,16 @@ import Prelude
115115
import Control.Alt ((<|>))
116116
import Control.Alternative (class Alternative)
117117
import Control.Lazy (class Lazy, defer)
118+
import Control.Monad.Eff (Eff, runPure)
118119
import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM2)
120+
import Control.Monad.ST (ST)
119121

122+
import Data.Array.ST (STArray, emptySTArray, pushSTArray, runSTArray')
123+
import Data.Array.ST.Iterator (iterate, iterator, pushWhile)
120124
import Data.Foldable (class Foldable, foldl, foldr)
121125
import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, elem, notElem, find, findMap, any, all) as Exports
122126
import Data.Maybe (Maybe(..), maybe, isJust, fromJust)
127+
import Data.Newtype (class Newtype, unwrap)
123128
import Data.NonEmpty (NonEmpty, (:|))
124129
import Data.Traversable (scanl, scanr) as Exports
125130
import Data.Traversable (sequence, traverse)
@@ -548,14 +553,25 @@ group' = group <<< sort
548553
-- | Group equal, consecutive elements of an array into arrays, using the
549554
-- | specified equivalence relation to detemine equality.
550555
groupBy :: forall a. (a -> a -> Boolean) -> Array a -> Array (NonEmpty Array a)
551-
groupBy op = go []
556+
groupBy op xs =
557+
runPure do
558+
runGroupedSTArray do
559+
result <- emptySTArray
560+
iter <- iterator (xs !! _)
561+
iterate iter \x -> do
562+
sub <- emptySTArray
563+
pushSTArray result (x :| sub)
564+
pushWhile (op x) iter sub
565+
pure result
552566
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
567+
runGroupedSTArray
568+
:: forall b r
569+
. (forall h. Eff (st :: ST h | r) (STArray h (NonEmpty (STArray h) b)))
570+
-> Eff r (Array (NonEmpty Array b))
571+
runGroupedSTArray a = map unwrap (runSTArray' (map Grouped a))
572+
573+
newtype Grouped a arr = Grouped (arr (NonEmpty arr a))
574+
derive instance newtypeGrouped :: Newtype (Grouped a arr) _
559575

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

test/Test/Data/Array.purs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ testArray = do
285285
assert $ A.group' [1, 2, 2, 3, 3, 3, 1] == [1 :| [1], 2 :| [2], 3 :| [3, 3]]
286286

287287
log "groupBy should group consecutive equal elements into arrays based on an equivalence relation"
288-
assert $ A.groupBy (\x y -> odd x && odd y) [1, 1, 2, 2, 3, 3] == [1 :| [1], NE.singleton 2, NE.singleton 2, 3 :| [3]]
288+
assert $ A.groupBy (\x y -> odd x && odd y) [1, 1, 2, 2, 3, 5] == [1 :| [1], NE.singleton 2, NE.singleton 2, 3 :| [5]]
289289

290290
log "nub should remove duplicate elements from the list, keeping the first occurence"
291291
assert $ A.nub [1, 2, 2, 3, 4, 1] == [1, 2, 3, 4]

0 commit comments

Comments
 (0)