Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 49 additions & 56 deletions src/Data/List.purs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ module Data.List
import Prelude

import Data.Maybe
import Data.Tuple (Tuple(..))
import Data.Tuple (Tuple(..), fst)
import Data.Monoid
import Data.Foldable
import Data.Unfoldable
Expand Down Expand Up @@ -134,31 +134,35 @@ infix 8 ..
(..) = range

-- | Create a list containing a range of integers, including both endpoints.
-- |
-- | Running time: `O(n)` where n is the difference of the arguments.
range :: Int -> Int -> List Int
range start end | start == end = singleton start
| otherwise = go end start (if start > end then 1 else -1) Nil
where
go s e step tail | s == e = (Cons s tail)
| otherwise = go (s + step) e step (Cons s tail)
range start end = go end start (if start > end then 1 else -1) Nil
where go s e step tail | s == e = (Cons s tail)
| otherwise = go (s + step) e step (Cons s tail)

-- | Create a list with repeated instances of a value.
-- |
-- | Running time: `O(n)` where n is first argument.
replicate :: forall a. Int -> a -> List a
replicate n value = go n Nil
where
go n tail | n <= 0 = tail
| otherwise = go (n - 1) (Cons value tail)

-- | Perform a monadic action `n` times collecting all of the results.
replicateM :: forall m a. (Monad m) => Int -> m a -> m (List a)
replicateM n m | n < one = return Nil
| otherwise = do a <- m
as <- replicateM (n - one) m
return (Cons a as)
-- |
-- | Running time: `O(n)` where n is first argument.
replicateM :: forall m a. (Applicative m) => Int -> m a -> m (List a)
replicateM n m | n < one = pure Nil
| otherwise = Cons <$> m <*> replicateM (n - one) m

-- | Attempt a computation multiple times, requiring at least one success.
-- |
-- | The `Lazy` constraint is used to generate the result lazily, to ensure
-- | termination.
-- |
-- | Running time: Depends on use because of lazy evaluation.
some :: forall f a. (Alternative f, Lazy (f (List a))) => f a -> f (List a)
some v = Cons <$> v <*> defer (\_ -> many v)

Expand All @@ -167,6 +171,8 @@ some v = Cons <$> v <*> defer (\_ -> many v)
-- |
-- | The `Lazy` constraint is used to generate the result lazily, to ensure
-- | termination.
-- |
-- | Running time: Depends on use because of lazy evaluation.
many :: forall f a. (Alternative f, Lazy (f (List a))) => f a -> f (List a)
many v = some v <|> pure Nil

Expand All @@ -185,8 +191,7 @@ null _ = false
-- |
-- | Running time: `O(n)`
length :: forall a. List a -> Int
length Nil = 0
length (Cons _ xs) = 1 + length xs
length = foldl (\z _ -> z + 1) 0

--------------------------------------------------------------------------------
-- Extending arrays ------------------------------------------------------------
Expand Down Expand Up @@ -239,9 +244,9 @@ head (Cons x _) = Just x
-- |
-- | Running time: `O(n)`.
last :: forall a. List a -> Maybe a
last Nil = Nothing
last (Cons x Nil) = Just x
last (Cons _ xs) = last xs
last _ = Nothing
last (Cons x xs) = last xs

-- | Get all but the first element of a list, or `Nothing` if the list is empty.
-- |
Expand Down Expand Up @@ -285,14 +290,20 @@ infixl 8 !!
(!!) = index

-- | Find the index of the first element equal to the specified element.
-- |
-- | Running time: `O(n)`
elemIndex :: forall a. (Eq a) => a -> List a -> Maybe Int
elemIndex x = findIndex (== x)

-- | Find the index of the last element equal to the specified element.
-- |
-- | Running time: `O(n)`
elemLastIndex :: forall a. (Eq a) => a -> List a -> Maybe Int
elemLastIndex x = findLastIndex (== x)

-- | Find the first index for which a predicate holds.
-- |
-- | Running time: `O(n)`
findIndex :: forall a. (a -> Boolean) -> List a -> Maybe Int
findIndex fn = go 0
where
Expand All @@ -302,8 +313,11 @@ findIndex fn = go 0
go _ Nil = Nothing

-- | Find the last index for which a predicate holds.
-- |
-- | Running time: `O(n)`
findLastIndex :: forall a. (a -> Boolean) -> List a -> Maybe Int
findLastIndex fn xs = ((length xs - 1) -) <$> findIndex fn (reverse xs)
findLastIndex fn = fst <<< foldl iter (Tuple Nothing 0)
where iter (Tuple _ n) x = Tuple (if fn x then Just n else Nothing) (n + 1)

-- | Insert an element into a list at the specified index, returning a new
-- | list or `Nothing` if the index is out-of-bounds.
Expand All @@ -312,25 +326,21 @@ findLastIndex fn xs = ((length xs - 1) -) <$> findIndex fn (reverse xs)
insertAt :: forall a. Int -> a -> List a -> Maybe (List a)
insertAt 0 x xs = Just (Cons x xs)
insertAt n x (Cons y ys) = Cons y <$> insertAt (n - 1) x ys
insertAt _ _ _ = Nothing
insertAt _ _ _ = Nothing

-- | Delete an element from a list at the specified index, returning a new
-- | list or `Nothing` if the index is out-of-bounds.
-- |
-- | Running time: `O(n)`
deleteAt :: forall a. Int -> List a -> Maybe (List a)
deleteAt 0 (Cons y ys) = Just ys
deleteAt n (Cons y ys) = Cons y <$> deleteAt (n - 1) ys
deleteAt _ _ = Nothing
deleteAt n = alterAt n (const Nothing)

-- | Update the element at the specified index, returning a new
-- | list or `Nothing` if the index is out-of-bounds.
-- |
-- | Running time: `O(n)`
updateAt :: forall a. Int -> a -> List a -> Maybe (List a)
updateAt 0 x (Cons _ xs) = Just (Cons x xs)
updateAt n x (Cons x1 xs) = Cons x1 <$> updateAt (n - 1) x xs
updateAt _ _ _ = Nothing
updateAt n x = alterAt n (const (Just x))

-- | Update the element at the specified index by applying a function to
-- | the current value, returning a new list or `Nothing` if the index is
Expand Down Expand Up @@ -361,35 +371,26 @@ alterAt _ _ _ = Nothing
-- |
-- | Running time: `O(n)`
reverse :: forall a. List a -> List a
reverse = go Nil
where
go acc Nil = acc
go acc (Cons x xs) = go (Cons x acc) xs
reverse = foldl (flip Cons) Nil

-- | Flatten a list of lists.
-- |
-- | Running time: `O(n)`, where `n` is the total number of elements.
concat :: forall a. List (List a) -> List a
concat = (>>= id)
concat = foldMap id

-- | Apply a function to each element in a list, and flatten the results
-- | into a single, new list.
-- |
-- | Running time: `O(n)`, where `n` is the total number of elements.
concatMap :: forall a b. (a -> List b) -> List a -> List b
concatMap _ Nil = Nil
concatMap f (Cons x xs) = f x <> concatMap f xs
concatMap = foldMap

-- | Filter a list, keeping the elements which satisfy a predicate function.
-- |
-- | Running time: `O(n)`
filter :: forall a. (a -> Boolean) -> List a -> List a
filter p = go Nil
where
go acc Nil = reverse acc
go acc (Cons x xs)
| p x = go (Cons x acc) xs
| otherwise = go acc xs
filter p = foldMap (\x -> if p x then singleton x else Nil)

-- | Filter where the predicate returns a monadic `Boolean`.
-- |
Expand All @@ -399,27 +400,17 @@ filter p = go Nil
-- | powerSet :: forall a. [a] -> [[a]]
-- | powerSet = filterM (const [true, false])
-- | ```
filterM :: forall a m. (Monad m) => (a -> m Boolean) -> List a -> m (List a)
filterM _ Nil = return Nil
filterM p (Cons x xs) = do
b <- p x
xs' <- filterM p xs
return $ if b
then Cons x xs'
else xs'
filterM :: forall a m. (Applicative m) => (a -> m Boolean) -> List a -> m (List a)
filterM _ Nil = pure Nil
filterM p (Cons x xs) = consIf <$> p x <*> filterM p xs
where consIf b xs' = if b then Cons x xs' else xs'

-- | Apply a function to each element in a list, keeping only the results which
-- | contain a value.
-- |
-- | Running time: `O(n)`
mapMaybe :: forall a b. (a -> Maybe b) -> List a -> List b
mapMaybe f = go Nil
where
go acc Nil = reverse acc
go acc (Cons x xs) =
case f x of
Nothing -> go acc xs
Just y -> go (Cons y acc) xs
mapMaybe f = foldMap (maybe Nil singleton <<< f)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this definitely still O(n)? It only is if the singletons are concatenated to the left of the result.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be the same as the above functions using foldMap, since foldMap is right fold, and concatenation to the singleton should be constant time. It does feel like a fair amount of wrapping/unwrapping, perhaps it would be better to define mapMaybe (and use it for filter as well)?


-- | Filter a list of optional values, keeping only the elements which contain
-- | a value.
Expand Down Expand Up @@ -476,6 +467,8 @@ sortBy cmp = mergeAll <<< sequences
--------------------------------------------------------------------------------

-- | Extract a sublist by a start and end index.
-- |
-- | Running time: `O(n + m)` where `n` and `m` are the start and end of the range.
slice :: forall a. Int -> Int -> List a -> List a
slice start end xs = take (end - start) (drop start xs)

Expand Down Expand Up @@ -558,8 +551,8 @@ group' = group <<< sort
-- | Running time: `O(n)`
groupBy :: forall a. (a -> a -> Boolean) -> List a -> List (List a)
groupBy _ Nil = Nil
groupBy eq (Cons x xs) = case span (eq x) xs of
{ init: ys, rest: zs } -> Cons (Cons x ys) (groupBy eq zs)
groupBy f (Cons x xs) = case span (f x) xs of
{ init: ys, rest: zs } -> Cons (Cons x ys) (groupBy f zs)

--------------------------------------------------------------------------------
-- Set-like operations ---------------------------------------------------------
Expand All @@ -577,7 +570,7 @@ nub = nubBy (==)
-- | Running time: `O(n^2)`
nubBy :: forall a. (a -> a -> Boolean) -> List a -> List a
nubBy _ Nil = Nil
nubBy (==) (Cons x xs) = Cons x (nubBy (==) (filter (\y -> not (x == y)) xs))
nubBy f (Cons x xs) = Cons x (nubBy f (filter (\y -> not (f x y)) xs))

-- | Calculate the union of two lists.
-- |
Expand All @@ -604,8 +597,8 @@ delete = deleteBy (==)
-- | Running time: `O(n)`
deleteBy :: forall a. (a -> a -> Boolean) -> a -> List a -> List a
deleteBy _ _ Nil = Nil
deleteBy (==) x (Cons y ys) | x == y = ys
deleteBy (==) x (Cons y ys) = Cons y (deleteBy (==) x ys)
deleteBy f x (Cons y ys) | f x y = ys
deleteBy f x (Cons y ys) = Cons y (deleteBy f x ys)

infix 5 \\

Expand Down