Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
20 changes: 14 additions & 6 deletions src/Data/List.purs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module Data.List
, tail
, init
, uncons
, unsnoc

, (!!), index
, elemIndex
Expand Down Expand Up @@ -242,12 +243,7 @@ tail (_ : xs) = Just xs
-- |
-- | Running time: `O(n)`
init :: forall a. List a -> Maybe (List a)
init Nil = Nothing
init lst = Just $ reverse $ go lst Nil
where
go (x : Nil) acc = acc
go (x : xs) acc = go xs (x : acc)
go _ acc = acc
init lst = _.init <$> unsnoc lst

-- | Break a list into its first element, and the remaining elements,
-- | or `Nothing` if the list is empty.
Expand All @@ -257,6 +253,18 @@ uncons :: forall a. List a -> Maybe { head :: a, tail :: List a }
uncons Nil = Nothing
uncons (x : xs) = Just { head: x, tail: xs }

-- | Break a list into its last element, and the preceding elements,
-- | or `Nothing` if the list is empty.
-- |
-- | Running time: `O(n)`
unsnoc :: forall a. List a -> Maybe { init :: List a, last :: a }
unsnoc lst = unsnocHelper lst Nil <#> \h -> { init: reverse h.revInit, last: h.last }

unsnocHelper :: forall a. List a -> List a -> Maybe { revInit :: List a, last :: a }
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please move this under a where? It could be called go - it's not the best name, but it's consistent with the other implementations here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure! The main reason I split it off was concerns about performance – I figured that redefining go on every call to unsnoc was wasteful. But I don't know much about that, so I'll take your advice.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know the extent to which JS engines optimize under functions, but I'd say it's better to be consistent at this point.

unsnocHelper Nil acc = Nothing
unsnocHelper (x : Nil) acc = Just { revInit: acc, last: x }
unsnocHelper (x : xs) acc = unsnocHelper xs (x : acc)

--------------------------------------------------------------------------------
-- Indexed operations ----------------------------------------------------------
--------------------------------------------------------------------------------
Expand Down
13 changes: 12 additions & 1 deletion test/Test/Data/List.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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, 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 ((:|))
Expand Down Expand Up @@ -106,6 +106,17 @@ testList = do
assert $ unsafePartial (fromJust u2).head == 1
assert $ unsafePartial (fromJust u2).tail == l [2, 3]

log "unsnoc should return nothing when used on an empty list"
assert $ isNothing (unsnoc nil)

log "unsnoc should split an list into an init and last record when there is at least one item"
let v1 = unsnoc (l [1])
assert $ unsafePartial (fromJust v1).init == l []
assert $ unsafePartial (fromJust v1).last == 1
let v2 = unsnoc (l [1, 2, 3])
assert $ unsafePartial (fromJust v2).init == l [1, 2]
assert $ unsafePartial (fromJust v2).last == 3

log "(!!) should return Just x when the index is within the bounds of the list"
assert $ l [1, 2, 3] !! 0 == (Just 1)
assert $ l [1, 2, 3] !! 1 == (Just 2)
Expand Down