From 8a6cc411c5f2c6f388bab8724fe442a8f2bbfe0f Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Tue, 12 Jan 2021 14:00:53 -0600 Subject: [PATCH 1/8] Copy Map from Data.Map.Internal --- src/Data/List/Internal.purs | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/Data/List/Internal.purs diff --git a/src/Data/List/Internal.purs b/src/Data/List/Internal.purs new file mode 100644 index 0000000..ddfcb33 --- /dev/null +++ b/src/Data/List/Internal.purs @@ -0,0 +1,63 @@ +module Data.List.Internal where + +import Prelude + +import Data.List.Types (List(..)) + +data Map k v + = Leaf + | Two (Map k v) k v (Map k v) + | Three (Map k v) k v (Map k v) k v (Map k v) + +data TreeContext k v + = TwoLeft k v (Map k v) + | TwoRight (Map k v) k v + | ThreeLeft k v (Map k v) k v (Map k v) + | ThreeMiddle (Map k v) k v k v (Map k v) + | ThreeRight (Map k v) k v (Map k v) k v + +fromZipper :: forall k v. Ord k => List (TreeContext k v) -> Map k v -> Map k v +fromZipper Nil tree = tree +fromZipper (Cons x ctx) tree = + case x of + TwoLeft k1 v1 right -> fromZipper ctx (Two tree k1 v1 right) + TwoRight left k1 v1 -> fromZipper ctx (Two left k1 v1 tree) + ThreeLeft k1 v1 mid k2 v2 right -> fromZipper ctx (Three tree k1 v1 mid k2 v2 right) + ThreeMiddle left k1 v1 k2 v2 right -> fromZipper ctx (Three left k1 v1 tree k2 v2 right) + ThreeRight left k1 v1 mid k2 v2 -> fromZipper ctx (Three left k1 v1 mid k2 v2 tree) + +data KickUp k v = KickUp (Map k v) k v (Map k v) + +-- | Insert or replace a key/value pair in a map +insert :: forall k v. Ord k => k -> v -> Map k v -> Map k v +insert k v = down Nil + where + comp :: k -> k -> Ordering + comp = compare + + down :: List (TreeContext k v) -> Map k v -> Map k v + down ctx Leaf = up ctx (KickUp Leaf k v Leaf) + down ctx (Two left k1 v1 right) = + case comp k k1 of + EQ -> fromZipper ctx (Two left k v right) + LT -> down (Cons (TwoLeft k1 v1 right) ctx) left + _ -> down (Cons (TwoRight left k1 v1) ctx) right + down ctx (Three left k1 v1 mid k2 v2 right) = + case comp k k1 of + EQ -> fromZipper ctx (Three left k v mid k2 v2 right) + c1 -> + case c1, comp k k2 of + _ , EQ -> fromZipper ctx (Three left k1 v1 mid k v right) + LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left + GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid + _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + + up :: List (TreeContext k v) -> KickUp k v -> Map k v + up Nil (KickUp left k' v' right) = Two left k' v' right + up (Cons x ctx) kup = + case x, kup of + TwoLeft k1 v1 right, KickUp left k' v' mid -> fromZipper ctx (Three left k' v' mid k1 v1 right) + TwoRight left k1 v1, KickUp mid k' v' right -> fromZipper ctx (Three left k1 v1 mid k' v' right) + ThreeLeft k1 v1 c k2 v2 d, KickUp a k' v' b -> up ctx (KickUp (Two a k' v' b) k1 v1 (Two c k2 v2 d)) + ThreeMiddle a k1 v1 k2 v2 d, KickUp b k' v' c -> up ctx (KickUp (Two a k1 v1 b) k' v' (Two c k2 v2 d)) + ThreeRight a k1 v1 b k2 v2, KickUp c k' v' d -> up ctx (KickUp (Two a k1 v1 b) k2 v2 (Two c k' v' d)) From 178f1a29c9b8499d0a3e4ec6e2895eaa4bfe007d Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Tue, 12 Jan 2021 14:03:36 -0600 Subject: [PATCH 2/8] Specialize to Set --- src/Data/List/Internal.purs | 74 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/Data/List/Internal.purs b/src/Data/List/Internal.purs index ddfcb33..786c849 100644 --- a/src/Data/List/Internal.purs +++ b/src/Data/List/Internal.purs @@ -4,60 +4,60 @@ import Prelude import Data.List.Types (List(..)) -data Map k v +data Set k = Leaf - | Two (Map k v) k v (Map k v) - | Three (Map k v) k v (Map k v) k v (Map k v) + | Two (Set k) k (Set k) + | Three (Set k) k (Set k) k (Set k) -data TreeContext k v - = TwoLeft k v (Map k v) - | TwoRight (Map k v) k v - | ThreeLeft k v (Map k v) k v (Map k v) - | ThreeMiddle (Map k v) k v k v (Map k v) - | ThreeRight (Map k v) k v (Map k v) k v +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 v. Ord k => List (TreeContext k v) -> Map k v -> Map k v +fromZipper :: forall k. Ord k => List (TreeContext k) -> Set k -> Set k fromZipper Nil tree = tree fromZipper (Cons x ctx) tree = case x of - TwoLeft k1 v1 right -> fromZipper ctx (Two tree k1 v1 right) - TwoRight left k1 v1 -> fromZipper ctx (Two left k1 v1 tree) - ThreeLeft k1 v1 mid k2 v2 right -> fromZipper ctx (Three tree k1 v1 mid k2 v2 right) - ThreeMiddle left k1 v1 k2 v2 right -> fromZipper ctx (Three left k1 v1 tree k2 v2 right) - ThreeRight left k1 v1 mid k2 v2 -> fromZipper ctx (Three left k1 v1 mid k2 v2 tree) + 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 v = KickUp (Map k v) k v (Map k v) +data KickUp k = KickUp (Set k) k (Set k) -- | Insert or replace a key/value pair in a map -insert :: forall k v. Ord k => k -> v -> Map k v -> Map k v -insert k v = down Nil +insert :: forall k. Ord k => k -> Set k -> Set k +insert k = down Nil where comp :: k -> k -> Ordering comp = compare - down :: List (TreeContext k v) -> Map k v -> Map k v - down ctx Leaf = up ctx (KickUp Leaf k v Leaf) - down ctx (Two left k1 v1 right) = + down :: List (TreeContext k) -> Set k -> Set k + down ctx Leaf = up ctx (KickUp Leaf k Leaf) + down ctx (Two left k1 right) = case comp k k1 of - EQ -> fromZipper ctx (Two left k v right) - LT -> down (Cons (TwoLeft k1 v1 right) ctx) left - _ -> down (Cons (TwoRight left k1 v1) ctx) right - down ctx (Three left k1 v1 mid k2 v2 right) = + EQ -> fromZipper ctx (Two left k right) + 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 -> fromZipper ctx (Three left k v mid k2 v2 right) + EQ -> fromZipper ctx (Three left k mid k2 right) c1 -> case c1, comp k k2 of - _ , EQ -> fromZipper ctx (Three left k1 v1 mid k v right) - LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left - GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + _ , EQ -> fromZipper ctx (Three left k1 mid k right) + 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 v) -> KickUp k v -> Map k v - up Nil (KickUp left k' v' right) = Two left k' v' 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 v1 right, KickUp left k' v' mid -> fromZipper ctx (Three left k' v' mid k1 v1 right) - TwoRight left k1 v1, KickUp mid k' v' right -> fromZipper ctx (Three left k1 v1 mid k' v' right) - ThreeLeft k1 v1 c k2 v2 d, KickUp a k' v' b -> up ctx (KickUp (Two a k' v' b) k1 v1 (Two c k2 v2 d)) - ThreeMiddle a k1 v1 k2 v2 d, KickUp b k' v' c -> up ctx (KickUp (Two a k1 v1 b) k' v' (Two c k2 v2 d)) - ThreeRight a k1 v1 b k2 v2, KickUp c k' v' d -> up ctx (KickUp (Two a k1 v1 b) k2 v2 (Two c k' v' d)) + 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)) From 7a39c9c497fda12220c46566b52a0f0f0ad40f99 Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Tue, 12 Jan 2021 14:06:48 -0600 Subject: [PATCH 3/8] Change to insertAndLookup function --- src/Data/List/Internal.purs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Data/List/Internal.purs b/src/Data/List/Internal.purs index 786c849..4dfc33d 100644 --- a/src/Data/List/Internal.purs +++ b/src/Data/List/Internal.purs @@ -1,4 +1,4 @@ -module Data.List.Internal where +module Data.List.Internal (Set, insertAndLookup) where import Prelude @@ -29,25 +29,25 @@ fromZipper (Cons x ctx) tree = data KickUp k = KickUp (Set k) k (Set k) -- | Insert or replace a key/value pair in a map -insert :: forall k. Ord k => k -> Set k -> Set k -insert k = down Nil +insertAndLookup :: forall k. Ord k => k -> Set k -> { found :: Boolean, result :: Set k } +insertAndLookup k orig = down Nil orig where comp :: k -> k -> Ordering comp = compare - down :: List (TreeContext k) -> Set k -> Set k - down ctx Leaf = up ctx (KickUp Leaf k Leaf) + 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 -> fromZipper ctx (Two left k right) + 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 -> fromZipper ctx (Three left k mid k2 right) + EQ -> { found: true, result: orig } c1 -> case c1, comp k k2 of - _ , EQ -> fromZipper ctx (Three left k1 mid k right) + _ , 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 From 7be77fdf5674ffa92302f3f53de32de61cd625dd Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Tue, 12 Jan 2021 14:26:53 -0600 Subject: [PATCH 4/8] Implement nub using Set --- src/Data/List.purs | 22 +++++++++------------- src/Data/List/Internal.purs | 14 +++++++------- src/Data/List/Lazy.purs | 25 +++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/Data/List.purs b/src/Data/List.purs index b5bcc79..fde1e62 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -108,6 +108,7 @@ 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.Function (on) 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(..)) @@ -117,7 +118,6 @@ import Data.Traversable (scanl, scanr) as Exports import Data.Traversable (sequence) import Data.Tuple (Tuple(..), fst, snd) import Data.Unfoldable (class Unfoldable, unfoldr) - import Prim.TypeError (class Warn, Text) -- | Convert a list into any unfoldable structure. @@ -685,18 +685,14 @@ nub = nubBy compare -- | -- | Running time: `O(n log n)` nubBy :: forall a. (a -> a -> Ordering) -> List a -> List a -nubBy p = - -- Discard indices, just keep original values. - mapReverse snd - -- Sort by index to recover original order. - -- Use `flip` to sort in reverse order in anticipation of final `mapReverse`. - <<< sortBy (flip compare `on` fst) - -- Removing neighboring duplicates. - <<< nubByAdjacentReverse (\a b -> (p `on` snd) a b == EQ) - -- Sort by original values to cluster duplicates. - <<< sortBy (p `on` snd) - -- Add indices so we can recover original order after deduplicating. - <<< addIndexReverse +nubBy p = go emptySet + where + go _ Nil = Nil + go s (a : as) = + let { found, result: s' } = insertAndLookupBy p a s + in if found + then go s' as + else a : go s' as -- | Remove duplicate elements from a list. -- | Keeps the first occurrence of each element in the input list, diff --git a/src/Data/List/Internal.purs b/src/Data/List/Internal.purs index 4dfc33d..5f5c950 100644 --- a/src/Data/List/Internal.purs +++ b/src/Data/List/Internal.purs @@ -1,4 +1,4 @@ -module Data.List.Internal (Set, insertAndLookup) where +module Data.List.Internal (Set, emptySet, insertAndLookupBy) where import Prelude @@ -9,6 +9,9 @@ data Set k | 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 @@ -16,7 +19,7 @@ data TreeContext k | ThreeMiddle (Set k) k k (Set k) | ThreeRight (Set k) k (Set k) k -fromZipper :: forall k. Ord k => List (TreeContext k) -> Set k -> Set k +fromZipper :: forall k. List (TreeContext k) -> Set k -> Set k fromZipper Nil tree = tree fromZipper (Cons x ctx) tree = case x of @@ -29,12 +32,9 @@ fromZipper (Cons x ctx) tree = data KickUp k = KickUp (Set k) k (Set k) -- | Insert or replace a key/value pair in a map -insertAndLookup :: forall k. Ord k => k -> Set k -> { found :: Boolean, result :: Set k } -insertAndLookup k orig = down Nil orig +insertAndLookupBy :: forall k. (k -> k -> Ordering) -> k -> Set k -> { found :: Boolean, result :: Set k } +insertAndLookupBy comp k orig = down Nil orig where - comp :: k -> k -> Ordering - comp = compare - 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) = diff --git a/src/Data/List/Lazy.purs b/src/Data/List/Lazy.purs index 9e28a4d..3b6ff84 100644 --- a/src/Data/List/Lazy.purs +++ b/src/Data/List/Lazy.purs @@ -103,6 +103,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,6 +591,30 @@ 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) + -- | Remove duplicate elements from a list. -- | -- | Running time: `O(n^2)` From 12732828140e663a17c85e28c762ef24c71a378d Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Tue, 12 Jan 2021 14:43:06 -0600 Subject: [PATCH 5/8] Remove unused functions --- src/Data/List.purs | 62 +--------------------------------------------- 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/src/Data/List.purs b/src/Data/List.purs index fde1e62..edd6a61 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -106,7 +106,6 @@ 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.Function (on) import Data.FunctorWithIndex (mapWithIndex) as FWI import Data.List.Internal (emptySet, insertAndLookupBy) import Data.List.Types (List(..), (:)) @@ -116,7 +115,7 @@ import Data.Newtype (class Newtype) import Data.NonEmpty ((:|)) import Data.Traversable (scanl, scanr) as Exports import Data.Traversable (sequence) -import Data.Tuple (Tuple(..), fst, snd) +import Data.Tuple (Tuple(..)) import Data.Unfoldable (class Unfoldable, unfoldr) import Prim.TypeError (class Warn, Text) @@ -841,62 +840,3 @@ transpose ((x : xs) : xss) = foldM :: forall m a b. Monad m => (b -> a -> m b) -> b -> List a -> m b foldM _ b Nil = pure b foldM f b (a : as) = f b a >>= \b' -> foldM f b' as - --------------------------------------------------------------------------------- --- Fast operations which also reverse the list --------------------------------- --------------------------------------------------------------------------------- - --- | Maps a function to each element in a list --- | and reverses the result, but faster than --- | running each separately. Equivalent to: --- | --- | ```purescript --- | \f l = map f l # reverse --- | ``` --- | --- | Running time: `O(n)` -mapReverse :: forall a b. (a -> b) -> List a -> List b -mapReverse f = go Nil - where - go :: List b -> List a -> List b - go acc Nil = acc - go acc (x : xs) = go (f x : acc) xs - --- | Converts each element to a Tuple containing its index, --- | and reverses the result, but faster than running separately. --- | Equivalent to: --- | --- | ```purescript --- | reverse <<< mapWithIndex Tuple --- | ``` --- | --- | Running time: `O(n)` -addIndexReverse :: forall a. List a -> List (Tuple Int a) -addIndexReverse = go 0 Nil - where - go :: Int -> List (Tuple Int a) -> List a -> List (Tuple Int a) - go i acc Nil = acc - go i acc (x : xs) = go (i + 1) ((Tuple i x) : acc) xs - --- | Removes neighboring duplicate items from a list --- | based on an equality predicate. --- | Keeps the LAST element if duplicates are encountered. --- | Returned list is reversed (this is to improve performance). --- | --- | ```purescript --- | nubByAdjacentReverse (on eq length) ([1]:[2]:[3,4]:Nil) == [3,4]:[2]:Nil` --- | ``` --- | --- | Running time: `O(n)` -nubByAdjacentReverse :: forall a. (a -> a -> Boolean) -> List a -> List a -nubByAdjacentReverse p = go Nil - where - go :: List a -> List a -> List a - -- empty output - go Nil (x : xs) = go (x : Nil) xs - -- checking for duplicates - go acc@(a : as) (x : xs) - | p a x = go (x : as) xs - | otherwise = go (x : acc) xs - -- empty input - go acc Nil = acc From c1373c2d82750ab64f48b95f1c6f42652675f305 Mon Sep 17 00:00:00 2001 From: Miles Frain Date: Tue, 12 Jan 2021 21:04:42 -0800 Subject: [PATCH 6/8] Add benchmarking for nub --- bench/Bench/Data/List.purs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bench/Bench/Data/List.purs b/bench/Bench/Data/List.purs index c40fe60..ab26c3e 100644 --- a/bench/Bench/Data/List.purs +++ b/bench/Bench/Data/List.purs @@ -3,7 +3,7 @@ module Bench.Data.List where import Prelude import Data.Foldable (maximum) import Data.Int (pow) -import Data.List (List(..), take, range, foldr, length) +import Data.List (List(..), take, range, foldr, length, nub) import Data.Maybe (fromMaybe) import Data.Traversable (traverse_) import Effect (Effect) @@ -12,8 +12,9 @@ import Performance.Minibench (bench) benchList :: Effect Unit benchList = do - benchLists "map" $ map (_ + 1) - benchLists "foldr" $ foldr add 0 + --benchLists "map" $ map (_ + 1) + --benchLists "foldr" $ foldr add 0 + benchLists "nub" nub where From a93bb427847561bce151cf5bd1715e8d3c74a70d Mon Sep 17 00:00:00 2001 From: Miles Frain Date: Tue, 12 Jan 2021 21:13:12 -0800 Subject: [PATCH 7/8] add benchmarking results --- ord-set-bench.txt | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 ord-set-bench.txt diff --git a/ord-set-bench.txt b/ord-set-bench.txt new file mode 100644 index 0000000..6ef3fb6 --- /dev/null +++ b/ord-set-bench.txt @@ -0,0 +1,83 @@ + +> @ bench /home/miles/projects/purescript/lists +> npm run bench:build && npm run bench:run + + +> @ bench:build /home/miles/projects/purescript/lists +> purs compile 'bench/**/*.purs' 'src/**/*.purs' 'bower_components/*/src/**/*.purs' + + +> @ bench:run /home/miles/projects/purescript/lists +> node --expose-gc -e 'require("./output/Bench.Main/index.js").main()' + +List +==== +--- +nub: list (0 elems) +mean = 1.07 μs +stddev = 3.20 μs +min = 602.00 ns +max = 101.06 μs +--- +nub: list (1 elems) +mean = 3.38 μs +stddev = 21.73 μs +min = 1.45 μs +max = 559.14 μs +--- +nub: list (10 elems) +mean = 26.75 μs +stddev = 80.87 μs +min = 3.45 μs +max = 1.37 ms +--- +nub: list (100 elems) +mean = 86.53 μs +stddev = 122.58 μs +min = 58.71 μs +max = 1.58 ms +--- +nub: list (1000 elems) +mean = 1.02 ms +stddev = 467.08 μs +min = 900.78 μs +max = 13.52 ms +--- +nub: list (10000 elems) +/home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:146 + function $tco_loop(v, tree) { + ^ + +RangeError: Maximum call stack size exceeded + at $tco_loop (/home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:146:27) + at /home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:182:27 + at $tco_loop (/home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:207:60) + at /home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:229:39 + at $tco_loop (/home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:244:48) + at /home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:299:39 + at /home/miles/projects/purescript/lists/output/Data.List.Internal/index.js:304:51 + at /home/miles/projects/purescript/lists/output/Data.List/index.js:507:76 + at /home/miles/projects/purescript/lists/output/Data.List/index.js:511:73 + at /home/miles/projects/purescript/lists/output/Data.List/index.js:511:73 +npm ERR! code ELIFECYCLE +npm ERR! errno 1 +npm ERR! @ bench:run: `node --expose-gc -e 'require("./output/Bench.Main/index.js").main()'` +npm ERR! Exit status 1 +npm ERR! +npm ERR! Failed at the @ bench:run script. +npm ERR! This is probably not a problem with npm. There is likely additional logging output above. +npm WARN Local package.json exists, but node_modules missing, did you mean to install? + +npm ERR! A complete log of this run can be found in: +npm ERR! /home/miles/.npm/_logs/2021-01-13T05_09_41_932Z-debug.log +npm ERR! code ELIFECYCLE +npm ERR! errno 1 +npm ERR! @ bench: `npm run bench:build && npm run bench:run` +npm ERR! Exit status 1 +npm ERR! +npm ERR! Failed at the @ bench script. +npm ERR! This is probably not a problem with npm. There is likely additional logging output above. +npm WARN Local package.json exists, but node_modules missing, did you mean to install? + +npm ERR! A complete log of this run can be found in: +npm ERR! /home/miles/.npm/_logs/2021-01-13T05_09_41_949Z-debug.log From 4115b8ec3af50f07dde73a1fc65c4bab7f8aa5fd Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Wed, 13 Jan 2021 03:11:12 -0600 Subject: [PATCH 8/8] Make stack-safe --- src/Data/List.purs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Data/List.purs b/src/Data/List.purs index edd6a61..b697202 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -684,14 +684,14 @@ nub = nubBy compare -- | -- | Running time: `O(n log n)` nubBy :: forall a. (a -> a -> Ordering) -> List a -> List a -nubBy p = go emptySet +nubBy p = reverse <<< go emptySet Nil where - go _ Nil = Nil - go s (a : as) = + go _ acc Nil = acc + go s acc (a : as) = let { found, result: s' } = insertAndLookupBy p a s in if found - then go s' as - else a : go s' as + 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,