-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Match nub* functions with Array #179
Changes from 23 commits
1998cd5
381a7e5
02f2b38
300bd31
25d4f49
83991e4
0871480
db10d08
6a84b72
ef26a93
8a6cc41
178f1a2
7a39c9c
7be77fd
1273282
214abe1
531eac4
0a3b051
9360df5
caf19f4
6318e76
1711f13
eb244d7
a76bcda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,8 @@ module Data.List | |
|
||
, nub | ||
, nubBy | ||
, nubEq | ||
, nubByEq | ||
, union | ||
, unionBy | ||
, delete | ||
|
@@ -101,22 +103,20 @@ import Control.Alt ((<|>)) | |
import Control.Alternative (class Alternative) | ||
import Control.Lazy (class Lazy, defer) | ||
import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM, tailRecM2) | ||
|
||
import Data.Bifunctor (bimap) | ||
import Data.Foldable (class Foldable, foldr, any, foldl) | ||
import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, elem, notElem, find, findMap, any, all) as Exports | ||
import Data.FunctorWithIndex (mapWithIndex) as FWI | ||
import Data.List.Internal (emptySet, insertAndLookupBy) | ||
import Data.List.Types (List(..), (:)) | ||
import Data.List.Types (NonEmptyList(..)) as NEL | ||
import Data.Maybe (Maybe(..)) | ||
import Data.Newtype (class Newtype) | ||
import Data.NonEmpty ((:|)) | ||
import Data.Traversable (scanl, scanr) as Exports | ||
import Data.Traversable (sequence) | ||
import Data.Tuple (Tuple(..)) | ||
import Data.Unfoldable (class Unfoldable, unfoldr) | ||
|
||
import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, elem, notElem, find, findMap, any, all) as Exports | ||
import Data.Traversable (scanl, scanr) as Exports | ||
|
||
import Prim.TypeError (class Warn, Text) | ||
|
||
-- | Convert a list into any unfoldable structure. | ||
|
@@ -663,18 +663,64 @@ tails list@(Cons _ tl)= list : tails tl | |
-------------------------------------------------------------------------------- | ||
|
||
-- | Remove duplicate elements from a list. | ||
-- | Keeps the first occurrence of each element in the input list, | ||
-- | in the same order they appear in the input list. | ||
-- | | ||
-- | ```purescript | ||
-- | nub 1:2:1:3:3:Nil == 1:2:3:Nil | ||
-- | ``` | ||
-- | | ||
-- | Running time: `O(n log n)` | ||
nub :: forall a. Ord a => List a -> List a | ||
nub = nubBy compare | ||
|
||
-- | Remove duplicate elements from a list based on the provided comparison function. | ||
-- | Keeps the first occurrence of each element in the input list, | ||
-- | in the same order they appear in the input list. | ||
-- | | ||
-- | ```purescript | ||
-- | nubBy (compare `on` Array.length) ([1]:[2]:[3,4]:Nil) == [1]:[3,4]:Nil | ||
-- | ``` | ||
-- | | ||
-- | Running time: `O(n log n)` | ||
nubBy :: forall a. (a -> a -> Ordering) -> List a -> List a | ||
nubBy p = reverse <<< go emptySet Nil | ||
where | ||
go _ acc Nil = acc | ||
go s acc (a : as) = | ||
let { found, result: s' } = insertAndLookupBy p a s | ||
in if found | ||
then go s' acc as | ||
else go s' (a : acc) as | ||
|
||
-- | Remove duplicate elements from a list. | ||
-- | Keeps the first occurrence of each element in the input list, | ||
-- | in the same order they appear in the input list. | ||
-- | This less efficient version of `nub` only requires an `Eq` instance. | ||
-- | | ||
-- | ```purescript | ||
-- | nubEq 1:2:1:3:3:Nil == 1:2:3:Nil | ||
-- | ``` | ||
-- | | ||
-- | Running time: `O(n^2)` | ||
nub :: forall a. Eq a => List a -> List a | ||
nub = nubBy eq | ||
nubEq :: forall a. Eq a => List a -> List a | ||
nubEq = nubByEq eq | ||
|
||
-- | Remove duplicate elements from a list, using the specified | ||
-- | function to determine equality of elements. | ||
-- | Remove duplicate elements from a list, using the provided equivalence function. | ||
-- | Keeps the first occurrence of each element in the input list, | ||
-- | in the same order they appear in the input list. | ||
-- | This less efficient version of `nubBy` only requires an equivalence | ||
-- | function, rather than an ordering function. | ||
-- | | ||
-- | ```purescript | ||
-- | mod3eq = eq `on` \n -> mod n 3 | ||
-- | nubByEq mod3eq 1:3:4:5:6:Nil == 1:3:5:Nil | ||
-- | ``` | ||
-- | | ||
-- | Running time: `O(n^2)` | ||
nubBy :: forall a. (a -> a -> Boolean) -> List a -> List a | ||
nubBy _ Nil = Nil | ||
nubBy eq' (x : xs) = x : nubBy eq' (filter (\y -> not (eq' x y)) xs) | ||
nubByEq :: forall a. (a -> a -> Boolean) -> List a -> List a | ||
nubByEq _ Nil = Nil | ||
nubByEq eq' (x : xs) = x : nubByEq eq' (filter (\y -> not (eq' x y)) xs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was never stack-safe. Reported in #194 |
||
|
||
-- | Calculate the union of two lists. | ||
-- | | ||
|
@@ -687,7 +733,7 @@ union = unionBy (==) | |
-- | | ||
-- | Running time: `O(n^2)` | ||
unionBy :: forall a. (a -> a -> Boolean) -> List a -> List a -> List a | ||
unionBy eq xs ys = xs <> foldl (flip (deleteBy eq)) (nubBy eq ys) xs | ||
milesfrain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
unionBy eq xs ys = xs <> foldl (flip (deleteBy eq)) (nubByEq eq ys) xs | ||
|
||
-- | Delete the first occurrence of an element from a list. | ||
-- | | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
module Data.List.Internal (Set, emptySet, insertAndLookupBy) where | ||
|
||
import Prelude | ||
|
||
import Data.List.Types (List(..)) | ||
|
||
data Set k | ||
= Leaf | ||
| Two (Set k) k (Set k) | ||
| Three (Set k) k (Set k) k (Set k) | ||
|
||
emptySet :: forall k. Set k | ||
emptySet = Leaf | ||
|
||
data TreeContext k | ||
= TwoLeft k (Set k) | ||
| TwoRight (Set k) k | ||
| ThreeLeft k (Set k) k (Set k) | ||
| ThreeMiddle (Set k) k k (Set k) | ||
| ThreeRight (Set k) k (Set k) k | ||
|
||
fromZipper :: forall k. List (TreeContext k) -> Set k -> Set k | ||
fromZipper Nil tree = tree | ||
fromZipper (Cons x ctx) tree = | ||
case x of | ||
TwoLeft k1 right -> fromZipper ctx (Two tree k1 right) | ||
TwoRight left k1 -> fromZipper ctx (Two left k1 tree) | ||
ThreeLeft k1 mid k2 right -> fromZipper ctx (Three tree k1 mid k2 right) | ||
ThreeMiddle left k1 k2 right -> fromZipper ctx (Three left k1 tree k2 right) | ||
ThreeRight left k1 mid k2 -> fromZipper ctx (Three left k1 mid k2 tree) | ||
|
||
data KickUp k = KickUp (Set k) k (Set k) | ||
|
||
-- | Insert or replace a key/value pair in a map | ||
insertAndLookupBy :: forall k. (k -> k -> Ordering) -> k -> Set k -> { found :: Boolean, result :: Set k } | ||
insertAndLookupBy comp k orig = down Nil orig | ||
where | ||
down :: List (TreeContext k) -> Set k -> { found :: Boolean, result :: Set k } | ||
down ctx Leaf = { found: false, result: up ctx (KickUp Leaf k Leaf) } | ||
down ctx (Two left k1 right) = | ||
case comp k k1 of | ||
EQ -> { found: true, result: orig } | ||
LT -> down (Cons (TwoLeft k1 right) ctx) left | ||
_ -> down (Cons (TwoRight left k1) ctx) right | ||
down ctx (Three left k1 mid k2 right) = | ||
case comp k k1 of | ||
EQ -> { found: true, result: orig } | ||
c1 -> | ||
case c1, comp k k2 of | ||
_ , EQ -> { found: true, result: orig } | ||
LT, _ -> down (Cons (ThreeLeft k1 mid k2 right) ctx) left | ||
GT, LT -> down (Cons (ThreeMiddle left k1 k2 right) ctx) mid | ||
_ , _ -> down (Cons (ThreeRight left k1 mid k2) ctx) right | ||
|
||
up :: List (TreeContext k) -> KickUp k -> Set k | ||
up Nil (KickUp left k' right) = Two left k' right | ||
up (Cons x ctx) kup = | ||
case x, kup of | ||
TwoLeft k1 right, KickUp left k' mid -> fromZipper ctx (Three left k' mid k1 right) | ||
TwoRight left k1, KickUp mid k' right -> fromZipper ctx (Three left k1 mid k' right) | ||
ThreeLeft k1 c k2 d, KickUp a k' b -> up ctx (KickUp (Two a k' b) k1 (Two c k2 d)) | ||
ThreeMiddle a k1 k2 d, KickUp b k' c -> up ctx (KickUp (Two a k1 b) k' (Two c k2 d)) | ||
ThreeRight a k1 b k2, KickUp c k' d -> up ctx (KickUp (Two a k1 b) k2 (Two c k' d)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,6 +72,8 @@ module Data.List.Lazy | |
|
||
, nub | ||
, nubBy | ||
, nubEq | ||
milesfrain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
, nubByEq | ||
, union | ||
, unionBy | ||
, delete | ||
|
@@ -103,6 +105,7 @@ import Control.Monad.Rec.Class as Rec | |
import Data.Foldable (class Foldable, foldr, any, foldl) | ||
import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, elem, notElem, find, findMap, any, all) as Exports | ||
import Data.Lazy (defer) | ||
import Data.List.Internal (emptySet, insertAndLookupBy) | ||
import Data.List.Lazy.Types (List(..), Step(..), step, nil, cons, (:)) | ||
import Data.List.Lazy.Types (NonEmptyList(..)) as NEL | ||
import Data.Maybe (Maybe(..), isNothing) | ||
|
@@ -590,21 +593,45 @@ partition f = foldr go {yes: nil, no: nil} | |
-- Set-like operations --------------------------------------------------------- | ||
-------------------------------------------------------------------------------- | ||
|
||
-- | Remove duplicate elements from a list. | ||
-- | Keeps the first occurrence of each element in the input list, | ||
-- | in the same order they appear in the input list. | ||
-- | | ||
-- | Running time: `O(n log n)` | ||
nub :: forall a. Ord a => List a -> List a | ||
nub = nubBy compare | ||
|
||
-- | Remove duplicate elements from a list based on the provided comparison function. | ||
-- | Keeps the first occurrence of each element in the input list, | ||
-- | in the same order they appear in the input list. | ||
-- | | ||
-- | Running time: `O(n log n)` | ||
nubBy :: forall a. (a -> a -> Ordering) -> List a -> List a | ||
nubBy p = go emptySet | ||
where | ||
go s (List l) = List (map (goStep s) l) | ||
goStep _ Nil = Nil | ||
goStep s (Cons a as) = | ||
let { found, result: s' } = insertAndLookupBy p a s | ||
in if found | ||
then step (go s' as) | ||
else Cons a (go s' as) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't look stack safe, but I don't know of a good way to fix it. The stack-safety strategy for strict There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is mostly stack-safe, because it's the caller's responsibility to force the thunks and most of the recursive calls are deferred. However, there could potentially be enough duplicate elements in a row to blow the stack (since we do recursively force thunks in that case). |
||
|
||
-- | Remove duplicate elements from a list. | ||
-- | | ||
-- | Running time: `O(n^2)` | ||
nub :: forall a. Eq a => List a -> List a | ||
nub = nubBy eq | ||
nubEq :: forall a. Eq a => List a -> List a | ||
nubEq = nubByEq eq | ||
|
||
-- | Remove duplicate elements from a list, using the specified | ||
-- | function to determine equality of elements. | ||
-- | | ||
-- | Running time: `O(n^2)` | ||
nubBy :: forall a. (a -> a -> Boolean) -> List a -> List a | ||
nubBy eq = List <<< map go <<< unwrap | ||
nubByEq :: forall a. (a -> a -> Boolean) -> List a -> List a | ||
nubByEq eq = List <<< map go <<< unwrap | ||
where | ||
go Nil = Nil | ||
go (Cons x xs) = Cons x (nubBy eq (filter (\y -> not (eq x y)) xs)) | ||
go (Cons x xs) = Cons x (nubByEq eq (filter (\y -> not (eq x y)) xs)) | ||
|
||
-- | Calculate the union of two lists. | ||
-- | | ||
|
@@ -617,7 +644,7 @@ union = unionBy (==) | |
-- | | ||
-- | Running time: `O(n^2)` | ||
unionBy :: forall a. (a -> a -> Boolean) -> List a -> List a -> List a | ||
unionBy eq xs ys = xs <> foldl (flip (deleteBy eq)) (nubBy eq ys) xs | ||
unionBy eq xs ys = xs <> foldl (flip (deleteBy eq)) (nubByEq eq ys) xs | ||
|
||
-- | Delete the first occurrence of an element from a list. | ||
-- | | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe consensus for changelog is to use past tense