From 399d48047f6bc8797119a7942148a38be8d8b037 Mon Sep 17 00:00:00 2001 From: Miles Frain Date: Tue, 12 Jan 2021 22:15:13 -0800 Subject: [PATCH 1/2] Attempting some stack safety strategies --- bench/Bench/Data/List.purs | 12 ++++++++---- src/Data/List.purs | 30 ++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/bench/Bench/Data/List.purs b/bench/Bench/Data/List.purs index ab26c3e..5c3f4fd 100644 --- a/bench/Bench/Data/List.purs +++ b/bench/Bench/Data/List.purs @@ -3,18 +3,22 @@ module Bench.Data.List where import Prelude import Data.Foldable (maximum) import Data.Int (pow) -import Data.List (List(..), take, range, foldr, length, nub) +import Data.List (List(..), take, range, foldr, length, nub, nubBy, nubBySafe, nubByAdjacentReverse) import Data.Maybe (fromMaybe) import Data.Traversable (traverse_) import Effect (Effect) import Effect.Console (log) -import Performance.Minibench (bench) +import Performance.Minibench (bench, benchWith) benchList :: Effect Unit benchList = do --benchLists "map" $ map (_ + 1) --benchLists "foldr" $ foldr add 0 - benchLists "nub" nub + + --benchLists "nub" nub -- not stack safe + --benchLists "nubByAdjacentReverse" $ nubByAdjacentReverse eq -- safe + --benchLists "nubBySafe" $ nubBySafe compare -- safe + benchLists "nubBy" $ nubBy compare -- not stack safe where @@ -30,4 +34,4 @@ benchList = do benchAList label func list = do log "---" log $ label <> ": list (" <> show (length list) <> " elems)" - bench \_ -> func list \ No newline at end of file + benchWith 1 \_ -> func list \ No newline at end of file diff --git a/src/Data/List.purs b/src/Data/List.purs index b5bcc79..9f8c7c3 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -73,6 +73,9 @@ module Data.List , groupAllBy , partition + , nubByAdjacentReverse + , nubBySafe + , nub , nubBy , nubEq @@ -698,6 +701,23 @@ nubBy p = -- Add indices so we can recover original order after deduplicating. <<< addIndexReverse +-- Same as nubBy, but eliminates nubByAdjacentReverse. +-- This is just for exploring stack safety. +-- It doesn't actually remove duplicates. +nubBySafe :: forall a. (a -> a -> Ordering) -> List a -> List a +nubBySafe 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 + -- | 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. @@ -860,7 +880,10 @@ foldM f b (a : as) = f b a >>= \b' -> foldM f b' as -- | -- | Running time: `O(n)` mapReverse :: forall a b. (a -> b) -> List a -> List b -mapReverse f = go Nil +mapReverse f l = reverse $ map f l + +mapReverse2 :: forall a b. (a -> b) -> List a -> List b +mapReverse2 f = go Nil where go :: List b -> List a -> List b go acc Nil = acc @@ -876,7 +899,10 @@ mapReverse f = go Nil -- | -- | Running time: `O(n)` addIndexReverse :: forall a. List a -> List (Tuple Int a) -addIndexReverse = go 0 Nil +addIndexReverse = reverse <<< mapWithIndex Tuple + +addIndexReverse2 :: forall a. List a -> List (Tuple Int a) +addIndexReverse2 = go 0 Nil where go :: Int -> List (Tuple Int a) -> List a -> List (Tuple Int a) go i acc Nil = acc From 8bae586e2e1a04552c4daf8987f964562c7ce753 Mon Sep 17 00:00:00 2001 From: Miles Frain Date: Tue, 12 Jan 2021 22:32:44 -0800 Subject: [PATCH 2/2] More tests, sortBy is the problem --- bench/Bench/Data/List.purs | 13 ++++++++----- src/Data/List.purs | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/bench/Bench/Data/List.purs b/bench/Bench/Data/List.purs index 5c3f4fd..7232d40 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, nub, nubBy, nubBySafe, nubByAdjacentReverse) +import Data.List (List(..), take, range, foldr, length, sortBy, addIndexReverse, mapReverse, nub, nubBy, nubBySafe, nubByAdjacentReverse) import Data.Maybe (fromMaybe) import Data.Traversable (traverse_) import Effect (Effect) @@ -16,13 +16,16 @@ benchList = do --benchLists "foldr" $ foldr add 0 --benchLists "nub" nub -- not stack safe - --benchLists "nubByAdjacentReverse" $ nubByAdjacentReverse eq -- safe - --benchLists "nubBySafe" $ nubBySafe compare -- safe - benchLists "nubBy" $ nubBy compare -- not stack safe + benchLists "nubByAdjacentReverse" $ nubByAdjacentReverse eq -- safe + benchLists "nubBySafe" $ nubBySafe compare -- safe + benchLists "addIndexReverse" addIndexReverse -- safe + benchLists "mapReverse" $ mapReverse $ add 1 -- safe + benchLists "sortBy" $ sortBy compare -- NOT SAFE!!!! + --benchLists "nubBy" $ nubBy compare -- not stack safe where - listSizes = Cons 0 $ map (pow 10) $ range 0 5 + listSizes = Cons 0 $ map (pow 10) $ range 0 6 nats = range 0 $ (fromMaybe 0 $ maximum listSizes) - 1 lists = map (\n -> take n nats) listSizes diff --git a/src/Data/List.purs b/src/Data/List.purs index 9f8c7c3..e55db88 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -74,6 +74,8 @@ module Data.List , partition , nubByAdjacentReverse + , addIndexReverse + , mapReverse , nubBySafe , nub