Skip to content

Commit 1c06da7

Browse files
committed
Remove use of uncons in implementation of span
1 parent 4c8bcd3 commit 1c06da7

File tree

2 files changed

+42
-9
lines changed

2 files changed

+42
-9
lines changed

src/Data/Array.purs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -493,18 +493,30 @@ dropWhile p xs = (span p xs).rest
493493
-- | ```purescript
494494
-- | span (\n -> n % 2 == 1) [1,3,2,4,5] == { init: [1,3], rest: [2,4,5] }
495495
-- | ```
496+
-- |
497+
-- | Running time: `O(n)`.
496498
span
497499
:: forall a
498500
. (a -> Boolean)
499501
-> Array a
500502
-> { init :: Array a, rest :: Array a }
501-
span p = go []
503+
span p arr =
504+
case breakIndex of
505+
Just 0 ->
506+
{ init: [], rest: arr }
507+
Just i ->
508+
{ init: slice 0 i arr, rest: slice i (length arr) arr }
509+
Nothing ->
510+
{ init: arr, rest: [] }
502511
where
503-
go :: Array a -> Array a -> { init :: Array a, rest :: Array a }
504-
go acc xs =
505-
case uncons xs of
506-
Just { head: x, tail: xs' } | p x -> go (x : acc) xs'
507-
_ -> { init: reverse acc, rest: xs }
512+
breakIndex = go 0
513+
go i =
514+
-- This looks like a good opportunity to use the Monad Maybe instance,
515+
-- but it's important to write out an explicit case expression here in
516+
-- order to ensure that TCO is triggered.
517+
case index arr i of
518+
Just x -> if p x then go (i+1) else Just i
519+
Nothing -> Nothing
508520

509521
-- | Group equal, consecutive elements of an array into arrays.
510522
-- |

test/Test/Data/Array.purs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,30 @@ testArray = do
237237
assert $ (drop (-2) [1, 2, 3]) == [1, 2, 3]
238238

239239
log "span should split an array in two based on a predicate"
240-
let spanResult = span (_ < 4) [1, 2, 3, 4, 5, 6, 7]
241-
assert $ spanResult.init == [1, 2, 3]
242-
assert $ spanResult.rest == [4, 5, 6, 7]
240+
let testSpan { p, input, init_, rest_ } = do
241+
let result = span p input
242+
assert $ result.init == init_
243+
assert $ result.rest == rest_
244+
245+
let oneToSeven = [1, 2, 3, 4, 5, 6, 7]
246+
testSpan { p: (_ < 4), input: oneToSeven, init_: [1, 2, 3], rest_: [4, 5, 6, 7] }
247+
248+
log "span with all elements satisfying the predicate"
249+
testSpan { p: const true, input: oneToSeven, init_: oneToSeven, rest_: [] }
250+
251+
log "span with no elements satisfying the predicate"
252+
testSpan { p: const false, input: oneToSeven, init_: [], rest_: oneToSeven }
253+
254+
log "span with large inputs: 10000"
255+
let testBigSpan n =
256+
testSpan { p: (_ < n), input: range 1 n, init_: range 1 (n-1), rest_: [n] }
257+
testBigSpan 10000
258+
259+
log "span with large inputs: 40000"
260+
testBigSpan 40000
261+
262+
log "span with large inputs: 100000"
263+
testBigSpan 100000
243264

244265
log "group should group consecutive equal elements into arrays"
245266
assert $ group [1, 2, 2, 3, 3, 3, 1] == [NE.singleton 1, 2 :| [2], 3:| [3, 3], NE.singleton 1]

0 commit comments

Comments
 (0)