From 801f53bcb9569c16ee9e89d067e4a3e61eea97e2 Mon Sep 17 00:00:00 2001 From: Matthew Leon Date: Tue, 30 May 2017 17:27:42 +0100 Subject: [PATCH 1/3] partition --- src/Data/List.purs | 12 ++++++++++++ src/Data/List/Lazy.purs | 10 ++++++++++ test/Test/Data/List.purs | 6 +++++- test/Test/Data/List/Lazy.purs | 6 +++++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Data/List.purs b/src/Data/List.purs index 5e06fe5..937c3fc 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -65,6 +65,7 @@ module Data.List , group , group' , groupBy + , partition , nub , nubBy @@ -561,6 +562,17 @@ groupBy _ Nil = Nil groupBy eq (x : xs) = case span (eq x) xs of { init: ys, rest: zs } -> NEL.NonEmptyList (x :| ys) : groupBy eq zs +-- | Returns a tuple of lists of elements which do +-- | and do not satisfy a predicate, respectively. +-- | +-- | Running time: `O(n)` +partition :: forall a. (a -> Boolean) -> List a -> Tuple (List a) (List a) +partition f xs = + case foldl go (Tuple Nil Nil) xs of + (Tuple ys' ns') -> Tuple (reverse ys') (reverse ns') + where + go (Tuple ys ns) x = if f x then Tuple (x : ys) ns else Tuple ys (x : ns) + -------------------------------------------------------------------------------- -- Set-like operations --------------------------------------------------------- -------------------------------------------------------------------------------- diff --git a/src/Data/List/Lazy.purs b/src/Data/List/Lazy.purs index eb748dc..2fe3039 100644 --- a/src/Data/List/Lazy.purs +++ b/src/Data/List/Lazy.purs @@ -66,6 +66,7 @@ module Data.List.Lazy , group -- , group' , groupBy + , partition , nub , nubBy @@ -558,6 +559,15 @@ groupBy eq = List <<< map go <<< unwrap { init: ys, rest: zs } -> Cons (NEL.NonEmptyList (defer \_ -> x :| ys)) (groupBy eq zs) +-- | Returns a tuple of lists of elements which do +-- | and do not satisfy a predicate, respectively. +-- | +-- | Running time: `O(n)` +partition :: forall a. (a -> Boolean) -> List a -> Tuple (List a) (List a) +partition f xs = foldr go (Tuple nil nil) xs + where + go x (Tuple ys ns) = if f x then Tuple (x : ys) ns else Tuple ys (x : ns) + -------------------------------------------------------------------------------- -- Set-like operations --------------------------------------------------------- -------------------------------------------------------------------------------- diff --git a/test/Test/Data/List.purs b/test/Test/Data/List.purs index fadb1eb..b1e7786 100644 --- a/test/Test/Data/List.purs +++ b/test/Test/Data/List.purs @@ -5,7 +5,7 @@ import Data.List.NonEmpty as NEL import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) import Data.Foldable (foldMap, foldl) -import Data.List (List(..), (..), length, range, foldM, unzip, zip, zipWithA, zipWith, intersectBy, intersect, (\\), deleteBy, delete, unionBy, union, nubBy, nub, groupBy, group', group, span, dropWhile, drop, takeWhile, take, sortBy, sort, catMaybes, mapMaybe, filterM, filter, concat, concatMap, reverse, alterAt, modifyAt, updateAt, deleteAt, insertAt, findLastIndex, findIndex, elemLastIndex, elemIndex, (!!), uncons, unsnoc, init, tail, last, head, insertBy, insert, snoc, null, singleton, fromFoldable, transpose, mapWithIndex, (:)) +import Data.List (List(..), (..), length, range, foldM, unzip, zip, zipWithA, zipWith, intersectBy, intersect, (\\), deleteBy, delete, unionBy, union, nubBy, nub, groupBy, group', group, partition, span, dropWhile, drop, takeWhile, take, sortBy, sort, catMaybes, mapMaybe, filterM, filter, concat, concatMap, reverse, alterAt, modifyAt, updateAt, deleteAt, insertAt, findLastIndex, findIndex, elemLastIndex, elemIndex, (!!), uncons, unsnoc, init, tail, last, head, insertBy, insert, snoc, null, singleton, fromFoldable, transpose, mapWithIndex, (:)) import Data.Maybe (Maybe(..), isNothing, fromJust) import Data.Monoid.Additive (Additive(..)) import Data.NonEmpty ((:|)) @@ -250,6 +250,10 @@ testList = do log "groupBy should group consecutive equal elements into lists based on an equivalence relation" assert $ groupBy (\x y -> odd x && odd y) (l [1, 1, 2, 2, 3, 3]) == l [NEL.NonEmptyList (1 :| l [1]), NEL.singleton 2, NEL.singleton 2, NEL.NonEmptyList (3 :| l [3])] + log "partition should separate a list into a tuple of lists that do and do not satisfy a predicate" + assert $ partition (_ > 2) (l [1, 5, 3, 2, 4]) == + Tuple (l [5, 3, 4]) (l [1, 2]) + log "nub should remove duplicate elements from the list, keeping the first occurence" assert $ nub (l [1, 2, 2, 3, 4, 1]) == l [1, 2, 3, 4] diff --git a/test/Test/Data/List/Lazy.purs b/test/Test/Data/List/Lazy.purs index 3f9cbc8..af1a565 100644 --- a/test/Test/Data/List/Lazy.purs +++ b/test/Test/Data/List/Lazy.purs @@ -7,7 +7,7 @@ import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) import Data.Lazy as Z -import Data.List.Lazy (List, nil, cons, foldl, foldr, foldMap, singleton, transpose, take, iterate, filter, uncons, foldM, range, unzip, zip, length, zipWithA, replicate, repeat, zipWith, intersectBy, intersect, deleteBy, delete, unionBy, union, nubBy, nub, groupBy, group, span, dropWhile, drop, takeWhile, slice, catMaybes, mapMaybe, filterM, concat, concatMap, reverse, alterAt, modifyAt, updateAt, deleteAt, insertAt, findLastIndex, findIndex, elemLastIndex, elemIndex, init, tail, last, head, insertBy, insert, snoc, null, replicateM, fromFoldable, (:), (\\), (!!)) +import Data.List.Lazy (List, nil, cons, foldl, foldr, foldMap, singleton, transpose, take, iterate, filter, uncons, foldM, range, unzip, zip, length, zipWithA, replicate, repeat, zipWith, intersectBy, intersect, deleteBy, delete, unionBy, union, nubBy, nub, groupBy, group, partition, span, dropWhile, drop, takeWhile, slice, catMaybes, mapMaybe, filterM, concat, concatMap, reverse, alterAt, modifyAt, updateAt, deleteAt, insertAt, findLastIndex, findIndex, elemLastIndex, elemIndex, init, tail, last, head, insertBy, insert, snoc, null, replicateM, fromFoldable, (:), (\\), (!!)) import Data.List.Lazy.NonEmpty as NEL import Data.Maybe (Maybe(..), isNothing, fromJust) import Data.Monoid.Additive (Additive(..)) @@ -270,6 +270,10 @@ testListLazy = do log "groupBy should group consecutive equal elements into lists based on an equivalence relation" assert $ groupBy (\x y -> odd x && odd y) (l [1, 1, 2, 2, 3, 3]) == l [nel (1 :| l [1]), NEL.singleton 2, NEL.singleton 2, nel (3 :| l [3])] + log "partition should separate a list into a tuple of lists that do and do not satisfy a predicate" + assert $ partition (_ > 2) (l [1, 5, 3, 2, 4]) == + Tuple (l [5, 3, 4]) (l [1, 2]) + log "iterate on nonempty lazy list should apply supplied function correctly" assert $ (take 3 $ NEL.toList $ NEL.iterate (_ + 1) 0) == l [0, 1, 2] From 79a0d1c822e7d1851f3adcaca289e9ecaec2f5fd Mon Sep 17 00:00:00 2001 From: Matthew Leon Date: Wed, 31 May 2017 09:32:36 +0100 Subject: [PATCH 2/3] partition: records instead of Tuples --- src/Data/List.purs | 12 ++++++------ src/Data/List/Lazy.purs | 7 ++++--- test/Test/Data/List.purs | 5 +++-- test/Test/Data/List/Lazy.purs | 5 +++-- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Data/List.purs b/src/Data/List.purs index 937c3fc..f3c9ead 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -562,16 +562,16 @@ groupBy _ Nil = Nil groupBy eq (x : xs) = case span (eq x) xs of { init: ys, rest: zs } -> NEL.NonEmptyList (x :| ys) : groupBy eq zs --- | Returns a tuple of lists of elements which do --- | and do not satisfy a predicate, respectively. +-- | Returns a lists of elements which do and do not satisfy a predicate. -- | -- | Running time: `O(n)` -partition :: forall a. (a -> Boolean) -> List a -> Tuple (List a) (List a) +partition :: forall a. (a -> Boolean) -> List a -> { yes :: List a, no :: List a } partition f xs = - case foldl go (Tuple Nil Nil) xs of - (Tuple ys' ns') -> Tuple (reverse ys') (reverse ns') + case foldl go {yes: Nil, no: Nil} xs of + {yes: ys, no: ns} -> {yes: reverse ys, no: reverse ns} where - go (Tuple ys ns) x = if f x then Tuple (x : ys) ns else Tuple ys (x : ns) + go {yes: ys, no: ns} x = + if f x then {yes: x : ys, no: ns} else {yes: ys, no: x : ns} -------------------------------------------------------------------------------- -- Set-like operations --------------------------------------------------------- diff --git a/src/Data/List/Lazy.purs b/src/Data/List/Lazy.purs index 2fe3039..4b0b222 100644 --- a/src/Data/List/Lazy.purs +++ b/src/Data/List/Lazy.purs @@ -563,10 +563,11 @@ groupBy eq = List <<< map go <<< unwrap -- | and do not satisfy a predicate, respectively. -- | -- | Running time: `O(n)` -partition :: forall a. (a -> Boolean) -> List a -> Tuple (List a) (List a) -partition f xs = foldr go (Tuple nil nil) xs +partition :: forall a. (a -> Boolean) -> List a -> { yes :: List a, no :: List a } +partition f = foldr go {yes: nil, no: nil} where - go x (Tuple ys ns) = if f x then Tuple (x : ys) ns else Tuple ys (x : ns) + go x {yes: ys, no: ns} = + if f x then {yes: x : ys, no: ns} else {yes: ys, no: x : ns} -------------------------------------------------------------------------------- -- Set-like operations --------------------------------------------------------- diff --git a/test/Test/Data/List.purs b/test/Test/Data/List.purs index b1e7786..0d33615 100644 --- a/test/Test/Data/List.purs +++ b/test/Test/Data/List.purs @@ -251,8 +251,9 @@ testList = do assert $ groupBy (\x y -> odd x && odd y) (l [1, 1, 2, 2, 3, 3]) == l [NEL.NonEmptyList (1 :| l [1]), NEL.singleton 2, NEL.singleton 2, NEL.NonEmptyList (3 :| l [3])] log "partition should separate a list into a tuple of lists that do and do not satisfy a predicate" - assert $ partition (_ > 2) (l [1, 5, 3, 2, 4]) == - Tuple (l [5, 3, 4]) (l [1, 2]) + let partitioned = partition (_ > 2) (l [1, 5, 3, 2, 4]) + assert $ partitioned.yes == l [5, 3, 4] + assert $ partitioned.no == l [1, 2] log "nub should remove duplicate elements from the list, keeping the first occurence" assert $ nub (l [1, 2, 2, 3, 4, 1]) == l [1, 2, 3, 4] diff --git a/test/Test/Data/List/Lazy.purs b/test/Test/Data/List/Lazy.purs index af1a565..f3e0a2b 100644 --- a/test/Test/Data/List/Lazy.purs +++ b/test/Test/Data/List/Lazy.purs @@ -271,8 +271,9 @@ testListLazy = do assert $ groupBy (\x y -> odd x && odd y) (l [1, 1, 2, 2, 3, 3]) == l [nel (1 :| l [1]), NEL.singleton 2, NEL.singleton 2, nel (3 :| l [3])] log "partition should separate a list into a tuple of lists that do and do not satisfy a predicate" - assert $ partition (_ > 2) (l [1, 5, 3, 2, 4]) == - Tuple (l [5, 3, 4]) (l [1, 2]) + let partitioned = partition (_ > 2) (l [1, 5, 3, 2, 4]) + assert $ partitioned.yes == l [5, 3, 4] + assert $ partitioned.no == l [1, 2] log "iterate on nonempty lazy list should apply supplied function correctly" assert $ (take 3 $ NEL.toList $ NEL.iterate (_ + 1) 0) == l [0, 1, 2] From 42c31f7be9fe3e7999f931f0f13835855269fe24 Mon Sep 17 00:00:00 2001 From: Matthew Leon Date: Fri, 2 Jun 2017 07:51:32 +0100 Subject: [PATCH 3/3] use foldr for strict partition --- src/Data/List.purs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Data/List.purs b/src/Data/List.purs index f3c9ead..69d5e6d 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -566,12 +566,11 @@ groupBy eq (x : xs) = case span (eq x) xs of -- | -- | Running time: `O(n)` partition :: forall a. (a -> Boolean) -> List a -> { yes :: List a, no :: List a } -partition f xs = - case foldl go {yes: Nil, no: Nil} xs of - {yes: ys, no: ns} -> {yes: reverse ys, no: reverse ns} +partition p xs = foldr select { no: Nil, yes: Nil } xs where - go {yes: ys, no: ns} x = - if f x then {yes: x : ys, no: ns} else {yes: ys, no: x : ns} + select x { no, yes } = if p x + then { no, yes: x : yes } + else { no: x : no, yes } -------------------------------------------------------------------------------- -- Set-like operations ---------------------------------------------------------