Idris2/libs/base/Data/List.idr

767 lines
23 KiB
Idris
Raw Normal View History

2020-05-18 15:59:07 +03:00
module Data.List
2020-07-29 12:51:07 +03:00
import Data.Nat
import Data.List1
import Data.Fin
2021-01-27 21:23:08 +03:00
import public Data.Zippable
2020-07-29 12:51:07 +03:00
%default total
2020-05-18 15:59:07 +03:00
public export
isNil : List a -> Bool
2020-07-08 02:55:55 +03:00
isNil [] = True
isNil _ = False
2020-05-18 15:59:07 +03:00
public export
isCons : List a -> Bool
2020-07-08 02:55:55 +03:00
isCons [] = False
isCons _ = True
2020-05-18 15:59:07 +03:00
public export
snoc : List a -> a -> List a
snoc xs x = xs ++ [x]
2020-05-18 15:59:07 +03:00
public export
take : Nat -> List a -> List a
take (S k) (x :: xs) = x :: take k xs
2020-06-30 02:32:18 +03:00
take _ _ = []
2020-05-18 15:59:07 +03:00
public export
drop : (n : Nat) -> (xs : List a) -> List a
drop Z xs = xs
drop (S n) [] = []
2020-07-08 02:55:55 +03:00
drop (S n) (_::xs) = drop n xs
2020-05-18 15:59:07 +03:00
||| Satisfiable if `k` is a valid index into `xs`.
|||
||| @ k the potential index
||| @ xs the list into which k may be an index
public export
data InBounds : (k : Nat) -> (xs : List a) -> Type where
||| Z is a valid index into any cons cell
InFirst : InBounds Z (x :: xs)
||| Valid indices can be extended
InLater : InBounds k xs -> InBounds (S k) (x :: xs)
public export
Uninhabited (InBounds k []) where
uninhabited InFirst impossible
uninhabited (InLater _) impossible
export
Uninhabited (InBounds k xs) => Uninhabited (InBounds (S k) (x::xs)) where
uninhabited (InLater y) = uninhabited y
||| Decide whether `k` is a valid index into `xs`.
public export
inBounds : (k : Nat) -> (xs : List a) -> Dec (InBounds k xs)
2020-07-08 02:55:55 +03:00
inBounds _ [] = No uninhabited
inBounds Z (_ :: _) = Yes InFirst
inBounds (S k) (x :: xs) with (inBounds k xs)
inBounds (S k) (x :: xs) | (Yes prf) = Yes (InLater prf)
inBounds (S k) (x :: xs) | (No contra)
2020-06-30 02:32:18 +03:00
= No $ \(InLater y) => contra y
||| Find a particular element of a list.
|||
||| @ ok a proof that the index is within bounds
public export
index : (n : Nat) -> (xs : List a) -> {auto 0 ok : InBounds n xs} -> a
index Z (x :: xs) {ok = InFirst} = x
2020-07-08 02:55:55 +03:00
index (S k) (_ :: xs) {ok = InLater _} = index k xs
public export
index' : (xs : List a) -> Fin (length xs) -> a
index' (x::_) FZ = x
index' (_::xs) (FS i) = index' xs i
2020-05-20 17:36:50 +03:00
||| Generate a list by repeatedly applying a partial function until exhausted.
||| @ f the function to iterate
||| @ x the initial value that will be the head of the list
covering
2020-05-20 17:36:50 +03:00
public export
iterate : (f : a -> Maybe a) -> (x : a) -> List a
iterate f x = x :: case f x of
Nothing => []
Just y => iterate f y
covering
public export
unfoldr : (b -> Maybe (a, b)) -> b -> List a
unfoldr f c = case f c of
Nothing => []
Just (a, n) => a :: unfoldr f n
public export
iterateN : Nat -> (a -> a) -> a -> List a
iterateN Z _ _ = []
iterateN (S n) f x = x :: iterateN n f (f x)
2020-05-18 15:59:07 +03:00
public export
takeWhile : (p : a -> Bool) -> List a -> List a
takeWhile p [] = []
takeWhile p (x::xs) = if p x then x :: takeWhile p xs else []
public export
dropWhile : (p : a -> Bool) -> List a -> List a
dropWhile p [] = []
dropWhile p (x::xs) = if p x then dropWhile p xs else x::xs
||| Applied to a predicate and a list, returns the list of those elements that
||| satisfy the predicate.
2020-05-18 15:59:07 +03:00
public export
filter : (p : a -> Bool) -> List a -> List a
filter p [] = []
filter p (x :: xs)
= if p x
then x :: filter p xs
else filter p xs
2020-05-27 13:21:29 +03:00
||| Find the first element of the list that satisfies the predicate.
public export
find : (p : a -> Bool) -> (xs : List a) -> Maybe a
find p [] = Nothing
find p (x::xs) = if p x then Just x else find p xs
||| Find the index and proof of InBounds of the first element (if exists) of a
||| list that satisfies the given test, else `Nothing`.
public export
findIndex : (a -> Bool) -> (xs : List a) -> Maybe $ Fin (length xs)
findIndex _ [] = Nothing
findIndex p (x :: xs) = if p x
then Just FZ
else FS <$> findIndex p xs
||| Find indices of all elements that satisfy the given test.
public export
findIndices : (a -> Bool) -> List a -> List Nat
findIndices p = h 0 where
h : Nat -> List a -> List Nat
h _ [] = []
h lvl (x :: xs) = if p x
then lvl :: h (S lvl) xs
else h (S lvl) xs
2020-05-18 15:59:07 +03:00
||| Find associated information in a list using a custom comparison.
public export
lookupBy : (a -> a -> Bool) -> a -> List (a, b) -> Maybe b
lookupBy p e [] = Nothing
2020-06-30 02:32:18 +03:00
lookupBy p e ((l, r) :: xs) =
if p e l then
Just r
else
lookupBy p e xs
2020-05-18 15:59:07 +03:00
||| Find associated information in a list using Boolean equality.
public export
lookup : Eq a => a -> List (a, b) -> Maybe b
lookup = lookupBy (==)
||| Check if something is a member of a list using a custom comparison.
public export
elemBy : (a -> a -> Bool) -> a -> List a -> Bool
elemBy p e [] = False
2020-07-08 02:55:55 +03:00
elemBy p e (x::xs) = p e x || elemBy p e xs
2020-05-18 15:59:07 +03:00
public export
nubBy : (a -> a -> Bool) -> List a -> List a
nubBy = nubBy' []
where
nubBy' : List a -> (a -> a -> Bool) -> List a -> List a
nubBy' acc p [] = []
nubBy' acc p (x::xs) =
if elemBy p x acc then
nubBy' acc p xs
else
x :: nubBy' (x::acc) p xs
||| O(n^2). The nub function removes duplicate elements from a list. In
||| particular, it keeps only the first occurrence of each element. It is a
||| special case of nubBy, which allows the programmer to supply their own
||| equality test.
|||
||| ```idris example
||| nub (the (List _) [1,2,1,3])
||| ```
public export
nub : Eq a => List a -> List a
nub = nubBy (==)
||| The deleteBy function behaves like delete, but takes a user-supplied equality predicate.
public export
deleteBy : (a -> a -> Bool) -> a -> List a -> List a
deleteBy _ _ [] = []
deleteBy eq x (y::ys) = if x `eq` y then ys else y :: deleteBy eq x ys
||| `delete x` removes the first occurrence of `x` from its list argument. For
||| example,
|||
|||````idris example
|||delete 'a' ['b', 'a', 'n', 'a', 'n', 'a']
|||````
|||
||| It is a special case of deleteBy, which allows the programmer to supply
||| their own equality test.
public export
delete : Eq a => a -> List a -> List a
delete = deleteBy (==)
||| The unionBy function returns the union of two lists by user-supplied equality predicate.
public export
unionBy : (a -> a -> Bool) -> List a -> List a -> List a
unionBy eq xs ys = xs ++ foldl (flip (deleteBy eq)) (nubBy eq ys) xs
||| Compute the union of two lists according to their `Eq` implementation.
|||
||| ```idris example
||| union ['d', 'o', 'g'] ['c', 'o', 'w']
||| ```
|||
public export
union : Eq a => List a -> List a -> List a
union = unionBy (==)
||| Like @span@ but using a predicate that might convert a to b, i.e. given a
||| predicate from a to Maybe b and a list of as, returns a tuple consisting of
||| the longest prefix of the list where a -> Just b, and the rest of the list.
public export
spanBy : (a -> Maybe b) -> List a -> (List b, List a)
spanBy p [] = ([], [])
spanBy p (x :: xs) = case p x of
Nothing => ([], x :: xs)
Just y => let (ys, zs) = spanBy p xs in (y :: ys, zs)
||| Given a predicate and a list, returns a tuple consisting of the longest
||| prefix of the list whose elements satisfy the predicate, and the rest of the
||| list.
2020-05-18 15:59:07 +03:00
public export
span : (a -> Bool) -> List a -> (List a, List a)
span p [] = ([], [])
span p (x::xs) =
if p x then
let (ys, zs) = span p xs in
(x::ys, zs)
2020-05-18 15:59:07 +03:00
else
([], x::xs)
public export
break : (a -> Bool) -> List a -> (List a, List a)
break p xs = span (not . p) xs
public export
split : (a -> Bool) -> List a -> List1 (List a)
2020-05-18 15:59:07 +03:00
split p xs =
case break p xs of
(chunk, []) => singleton chunk
2021-03-17 17:07:52 +03:00
(chunk, (c :: rest)) => chunk ::: forget (split p (assert_smaller xs rest))
2020-05-18 15:59:07 +03:00
public export
splitAt : (n : Nat) -> (xs : List a) -> (List a, List a)
splitAt Z xs = ([], xs)
splitAt (S k) [] = ([], [])
splitAt (S k) (x :: xs)
= let (tk, dr) = splitAt k xs in
(x :: tk, dr)
public export
partition : (a -> Bool) -> List a -> (List a, List a)
partition p [] = ([], [])
partition p (x::xs) =
let (lefts, rights) = partition p xs in
if p x then
(x::lefts, rights)
else
(lefts, x::rights)
||| The inits function returns all initial segments of the argument, shortest
||| first. For example,
|||
||| ```idris example
||| inits [1,2,3]
||| ```
public export
inits : List a -> List (List a)
inits xs = [] :: case xs of
[] => []
x :: xs' => map (x ::) (inits xs')
||| The tails function returns all final segments of the argument, longest
||| first. For example,
|||
||| ```idris example
||| tails [1,2,3] == [[1,2,3], [2,3], [3], []]
|||```
public export
tails : List a -> List (List a)
tails xs = xs :: case xs of
[] => []
_ :: xs' => tails xs'
||| Split on the given element.
|||
||| ```idris example
||| splitOn 0 [1,0,2,0,0,3]
||| ```
|||
public export
splitOn : Eq a => a -> List a -> List1 (List a)
2020-05-18 15:59:07 +03:00
splitOn a = split (== a)
2020-11-27 22:10:08 +03:00
public export
replaceWhen : (a -> Bool) -> a -> List a -> List a
replaceWhen p b l = map (\c => if p c then b else c) l
2020-05-18 15:59:07 +03:00
||| Replaces all occurences of the first argument with the second argument in a list.
|||
||| ```idris example
||| replaceOn '-' ',' ['1', '-', '2', '-', '3']
||| ```
|||
public export
replaceOn : Eq a => a -> a -> List a -> List a
2020-11-27 22:10:08 +03:00
replaceOn a = replaceWhen (== a)
2020-05-18 15:59:07 +03:00
public export
reverseOnto : List a -> List a -> List a
reverseOnto acc [] = acc
reverseOnto acc (x::xs) = reverseOnto (x::acc) xs
public export
reverse : List a -> List a
reverse = reverseOnto []
||| Construct a list with `n` copies of `x`.
||| @ n how many copies
||| @ x the element to replicate
public export
replicate : (n : Nat) -> (x : a) -> List a
replicate Z _ = []
replicate (S n) x = x :: replicate n x
||| Compute the intersect of two lists by user-supplied equality predicate.
export
intersectBy : (a -> a -> Bool) -> List a -> List a -> List a
intersectBy eq xs ys = [x | x <- xs, any (eq x) ys]
||| Compute the intersect of two lists according to the `Eq` implementation for the elements.
export
intersect : Eq a => List a -> List a -> List a
intersect = intersectBy (==)
export
intersectAllBy : (a -> a -> Bool) -> List (List a) -> List a
intersectAllBy eq [] = []
intersectAllBy eq (xs :: xss) = filter (\x => all (elemBy eq x) xss) xs
export
intersectAll : Eq a => List (List a) -> List a
intersectAll = intersectAllBy (==)
2021-01-27 21:23:08 +03:00
---------------------------
-- Zippable --
---------------------------
public export
2021-01-27 21:23:08 +03:00
Zippable List where
zipWith _ [] _ = []
zipWith _ _ [] = []
zipWith f (x :: xs) (y :: ys) = f x y :: zipWith f xs ys
zipWith3 _ [] _ _ = []
zipWith3 _ _ [] _ = []
zipWith3 _ _ _ [] = []
zipWith3 f (x :: xs) (y :: ys) (z :: zs) = f x y z :: zipWith3 f xs ys zs
unzipWith f [] = ([], [])
unzipWith f (x :: xs) = let (b, c) = f x
(bs, cs) = unzipWith f xs in
(b :: bs, c :: cs)
unzipWith3 f [] = ([], [], [])
unzipWith3 f (x :: xs) = let (b, c, d) = f x
(bs, cs, ds) = unzipWith3 f xs in
(b :: bs, c :: cs, d :: ds)
2021-03-17 17:07:52 +03:00
---------------------------
-- Non-empty List
---------------------------
2020-05-18 15:59:07 +03:00
public export
data NonEmpty : (xs : List a) -> Type where
IsNonEmpty : NonEmpty (x :: xs)
export
Uninhabited (NonEmpty []) where
uninhabited IsNonEmpty impossible
||| Get the head of a non-empty list.
||| @ ok proof the list is non-empty
public export
head : (l : List a) -> {auto 0 ok : NonEmpty l} -> a
2020-05-18 15:59:07 +03:00
head [] impossible
2020-07-08 02:55:55 +03:00
head (x :: _) = x
2020-05-18 15:59:07 +03:00
||| Get the tail of a non-empty list.
||| @ ok proof the list is non-empty
public export
tail : (l : List a) -> {auto 0 ok : NonEmpty l} -> List a
2020-05-18 15:59:07 +03:00
tail [] impossible
2020-06-30 02:32:18 +03:00
tail (_ :: xs) = xs
2020-05-18 15:59:07 +03:00
2020-05-20 17:36:50 +03:00
||| Retrieve the last element of a non-empty list.
||| @ ok proof that the list is non-empty
public export
last : (l : List a) -> {auto 0 ok : NonEmpty l} -> a
2020-05-20 17:36:50 +03:00
last [] impossible
last [x] = x
2021-03-17 17:07:52 +03:00
last (x :: xs@(_::_)) = last xs
2020-05-20 17:36:50 +03:00
||| Return all but the last element of a non-empty list.
||| @ ok proof the list is non-empty
public export
init : (l : List a) -> {auto 0 ok : NonEmpty l} -> List a
2020-05-20 17:36:50 +03:00
init [] impossible
2021-03-17 17:07:52 +03:00
init [x] = []
init (x :: xs@(_::_)) = x :: init xs
2020-05-20 17:36:50 +03:00
2020-05-18 15:59:07 +03:00
||| Attempt to get the head of a list. If the list is empty, return `Nothing`.
2020-06-10 21:43:38 +03:00
public export
2020-05-18 15:59:07 +03:00
head' : List a -> Maybe a
head' [] = Nothing
2020-06-30 02:32:18 +03:00
head' (x::_) = Just x
2020-05-18 15:59:07 +03:00
||| Attempt to get the tail of a list. If the list is empty, return `Nothing`.
export
tail' : List a -> Maybe (List a)
tail' [] = Nothing
2020-06-30 02:32:18 +03:00
tail' (_::xs) = Just xs
2020-05-18 15:59:07 +03:00
2020-05-20 17:36:50 +03:00
||| Attempt to retrieve the last element of a non-empty list.
|||
||| If the list is empty, return `Nothing`.
export
last' : List a -> Maybe a
last' [] = Nothing
last' xs@(_::_) = Just (last xs)
||| Attempt to return all but the last element of a non-empty list.
|||
||| If the list is empty, return `Nothing`.
export
init' : List a -> Maybe (List a)
init' [] = Nothing
init' xs@(_::_) = Just (init xs)
2021-03-17 17:07:52 +03:00
public export
foldr1By : (func : a -> b -> b) -> (map : a -> b) ->
(l : List a) -> {auto 0 ok : NonEmpty l} -> b
foldr1By f map [] impossible
foldr1By f map [x] = map x
foldr1By f map (x :: xs@(_::_)) = f x (foldr1By f map xs)
public export
foldl1By : (func : b -> a -> b) -> (map : a -> b) ->
(l : List a) -> {auto 0 ok : NonEmpty l} -> b
foldl1By f map [] impossible
foldl1By f map (x::xs) = foldl f (map x) xs
||| Foldr a non-empty list without seeding the accumulator.
||| @ ok proof that the list is non-empty
public export
foldr1 : (a -> a -> a) -> (l : List a) -> {auto 0 ok : NonEmpty l} -> a
foldr1 f xs = foldr1By f id xs
||| Foldl a non-empty list without seeding the accumulator.
||| @ ok proof that the list is non-empty
public export
foldl1 : (a -> a -> a) -> (l : List a) -> {auto 0 ok : NonEmpty l} -> a
foldl1 f xs = foldl1By f id xs
||| Convert to a non-empty list.
||| @ ok proof the list is non-empty
export
toList1 : (l : List a) -> {auto 0 ok : NonEmpty l} -> List1 a
toList1 [] impossible
toList1 (x :: xs) = x ::: xs
||| Convert to a non-empty list, returning Nothing if the list is empty.
2020-05-18 15:59:07 +03:00
export
2021-03-17 17:07:52 +03:00
toList1' : (l : List a) -> Maybe (List1 a)
toList1' [] = Nothing
toList1' (x :: xs) = Just (x ::: xs)
2020-05-18 15:59:07 +03:00
||| Prefix every element in the list with the given element
|||
||| ```idris example
||| with List (mergeReplicate '>' ['a', 'b', 'c', 'd', 'e'])
||| ```
|||
export
mergeReplicate : a -> List a -> List a
mergeReplicate sep [] = []
mergeReplicate sep (y::ys) = sep :: y :: mergeReplicate sep ys
||| Insert some separator between the elements of a list.
|||
||| ````idris example
||| with List (intersperse ',' ['a', 'b', 'c', 'd', 'e'])
||| ````
|||
export
intersperse : a -> List a -> List a
intersperse sep [] = []
intersperse sep (x::xs) = x :: mergeReplicate sep xs
||| Given a separator list and some more lists, produce a new list by
||| placing the separator between each of the lists.
|||
||| @ sep the separator
||| @ xss the lists between which the separator will be placed
|||
||| ```idris example
||| intercalate [0, 0, 0] [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]
||| ```
export
intercalate : (sep : List a) -> (xss : List (List a)) -> List a
intercalate sep xss = concat $ intersperse sep xss
||| Apply a partial function to the elements of a list, keeping the ones at which
||| it is defined.
2020-06-10 21:43:38 +03:00
public export
2020-05-18 15:59:07 +03:00
mapMaybe : (a -> Maybe b) -> List a -> List b
mapMaybe f [] = []
mapMaybe f (x::xs) =
case f x of
Nothing => mapMaybe f xs
Just j => j :: mapMaybe f xs
||| Extract all of the values contained in a List of Maybes
public export
catMaybes : List (Maybe a) -> List a
catMaybes = mapMaybe id
2020-05-18 15:59:07 +03:00
--------------------------------------------------------------------------------
-- Sorting
--------------------------------------------------------------------------------
||| Check whether a list is sorted with respect to the default ordering for the type of its elements.
export
sorted : Ord a => List a -> Bool
2020-06-30 02:32:18 +03:00
sorted (x :: xs @ (y :: _)) = x <= y && sorted xs
sorted _ = True
2020-05-18 15:59:07 +03:00
||| Merge two sorted lists using an arbitrary comparison
||| predicate. Note that the lists must have been sorted using this
||| predicate already.
export
mergeBy : (a -> a -> Ordering) -> List a -> List a -> List a
mergeBy order [] right = right
mergeBy order left [] = left
mergeBy order (x::xs) (y::ys) =
-- The code below will emit `y` before `x` whenever `x == y`.
-- If you change this, `sortBy` will stop being stable, unless you fix `sortBy`, too.
case order x y of
LT => x :: mergeBy order xs (y::ys)
_ => y :: mergeBy order (x::xs) ys
||| Merge two sorted lists using the default ordering for the type of their elements.
export
merge : Ord a => List a -> List a -> List a
merge left right = mergeBy compare left right
||| Sort a list using some arbitrary comparison predicate.
|||
||| @ cmp how to compare elements
||| @ xs the list to sort
export
sortBy : (cmp : a -> a -> Ordering) -> (xs : List a) -> List a
sortBy cmp [] = []
sortBy cmp [x] = [x]
sortBy cmp xs = let (x, y) = split xs in
mergeBy cmp
(sortBy cmp (assert_smaller xs x))
(sortBy cmp (assert_smaller xs y)) -- not structurally smaller, hence assert
where
splitRec : List b -> List a -> (List a -> List a) -> (List a, List a)
splitRec (_::_::xs) (y::ys) zs = splitRec xs ys (zs . ((::) y))
splitRec _ ys zs = (ys, zs [])
-- In the above base-case clause, we put `ys` on the LHS to get a stable sort.
-- This is because `mergeBy` prefers taking elements from its RHS operand
-- if both heads are equal, and all elements in `zs []` precede all elements of `ys`
-- in the original list.
split : List a -> (List a, List a)
split xs = splitRec xs xs id
||| Sort a list using the default ordering for the type of its elements.
export
sort : Ord a => List a -> List a
sort = sortBy compare
export
isPrefixOfBy : (eq : a -> a -> Bool) -> (left, right : List a) -> Bool
2020-06-30 02:32:18 +03:00
isPrefixOfBy p [] _ = True
isPrefixOfBy p _ [] = False
isPrefixOfBy p (x::xs) (y::ys) = p x y && isPrefixOfBy p xs ys
2020-05-18 15:59:07 +03:00
||| The isPrefixOf function takes two lists and returns True iff the first list is a prefix of the second.
export
isPrefixOf : Eq a => List a -> List a -> Bool
isPrefixOf = isPrefixOfBy (==)
export
isSuffixOfBy : (a -> a -> Bool) -> List a -> List a -> Bool
isSuffixOfBy p left right = isPrefixOfBy p (reverse left) (reverse right)
||| The isSuffixOf function takes two lists and returns True iff the first list is a suffix of the second.
export
isSuffixOf : Eq a => List a -> List a -> Bool
isSuffixOf = isSuffixOfBy (==)
||| The isInfixOf function takes two lists and returns True iff the first list
||| is contained, wholly and intact, anywhere within the second.
|||
||| ```idris example
||| isInfixOf ['b','c'] ['a', 'b', 'c', 'd']
||| ```
||| ```idris example
||| isInfixOf ['b','d'] ['a', 'b', 'c', 'd']
||| ```
|||
export
isInfixOf : Eq a => List a -> List a -> Bool
isInfixOf n h = any (isPrefixOf n) (tails h)
||| Transposes rows and columns of a list of lists.
|||
||| ```idris example
||| with List transpose [[1, 2], [3, 4]]
||| ```
|||
||| This also works for non square scenarios, thus
||| involution does not always hold:
|||
||| transpose [[], [1, 2]] = [[1], [2]]
||| transpose (transpose [[], [1, 2]]) = [[1, 2]]
export
transpose : List (List a) -> List (List a)
transpose [] = []
transpose (heads :: tails) = spreadHeads heads (transpose tails) where
spreadHeads : List a -> List (List a) -> List (List a)
spreadHeads [] tails = tails
spreadHeads (head :: heads) [] = [head] :: spreadHeads heads []
spreadHeads (head :: heads) (tail :: tails) = (head :: tail) :: spreadHeads heads tails
------------------------------------------------------------------------
-- Grouping
||| `groupBy` operates like `group`, but uses the provided equality
||| predicate instead of `==`.
public export
groupBy : (a -> a -> Bool) -> List a -> List (List1 a)
groupBy _ [] = []
groupBy eq (h :: t) = let (ys,zs) = go h t
in ys :: zs
where go : a -> List a -> (List1 a, List (List1 a))
go v [] = (singleton v,[])
go v (x :: xs) = let (ys,zs) = go x xs
in if eq v x
then (cons v ys, zs)
else (singleton v, ys :: zs)
||| The `group` function takes a list of values and returns a list of
||| lists such that flattening the resulting list is equal to the
||| argument. Moreover, each list in the resulting list
||| contains only equal elements.
public export
group : Eq a => List a -> List (List1 a)
group = groupBy (==)
||| `groupWith` operates like `group`, but uses the provided projection when
||| comparing for equality
public export
groupWith : Eq b => (a -> b) -> List a -> List (List1 a)
groupWith f = groupBy (\x,y => f x == f y)
||| `groupAllWith` operates like `groupWith`, but sorts the list
||| first so that each equivalence class has, at most, one list in the
||| output
public export
groupAllWith : Ord b => (a -> b) -> List a -> List (List1 a)
groupAllWith f = groupWith f . sortBy (comparing f)
2020-05-18 15:59:07 +03:00
--------------------------------------------------------------------------------
-- Properties
--------------------------------------------------------------------------------
export
Uninhabited ([] = x :: xs) where
2020-05-18 15:59:07 +03:00
uninhabited Refl impossible
export
Uninhabited (x :: xs = []) where
2020-05-18 15:59:07 +03:00
uninhabited Refl impossible
export
{0 xs : List a} -> Either (Uninhabited $ x === y) (Uninhabited $ xs === ys) => Uninhabited (x::xs = y::ys) where
uninhabited @{Left z} Refl = uninhabited @{z} Refl
uninhabited @{Right z} Refl = uninhabited @{z} Refl
2020-06-30 04:18:40 +03:00
||| (::) is injective
export
consInjective : forall x, xs, y, ys .
the (List a) (x :: xs) = the (List b) (y :: ys) -> (x = y, xs = ys)
2020-06-30 04:18:40 +03:00
consInjective Refl = (Refl, Refl)
2020-05-18 15:59:07 +03:00
||| The empty list is a right identity for append.
export
2020-07-08 02:55:55 +03:00
appendNilRightNeutral : (l : List a) -> l ++ [] = l
2020-05-18 15:59:07 +03:00
appendNilRightNeutral [] = Refl
2020-07-08 02:55:55 +03:00
appendNilRightNeutral (_::xs) = rewrite appendNilRightNeutral xs in Refl
2020-05-18 15:59:07 +03:00
||| Appending lists is associative.
export
2020-07-08 02:55:55 +03:00
appendAssociative : (l, c, r : List a) -> l ++ (c ++ r) = (l ++ c) ++ r
2020-05-18 15:59:07 +03:00
appendAssociative [] c r = Refl
2020-07-08 02:55:55 +03:00
appendAssociative (_::xs) c r = rewrite appendAssociative xs c r in Refl
2020-05-18 15:59:07 +03:00
revOnto : (xs, vs : List a) -> reverseOnto xs vs = reverse vs ++ xs
2020-07-08 02:55:55 +03:00
revOnto _ [] = Refl
2020-05-18 15:59:07 +03:00
revOnto xs (v :: vs)
= rewrite revOnto (v :: xs) vs in
rewrite appendAssociative (reverse vs) [v] xs in
rewrite revOnto [v] vs in Refl
2020-05-18 15:59:07 +03:00
export
revAppend : (vs, ns : List a) -> reverse ns ++ reverse vs = reverse (vs ++ ns)
revAppend [] ns = rewrite appendNilRightNeutral (reverse ns) in Refl
revAppend (v :: vs) ns
= rewrite revOnto [v] vs in
rewrite revOnto [v] (vs ++ ns) in
rewrite sym (revAppend vs ns) in
rewrite appendAssociative (reverse ns) (reverse vs) [v] in
Refl
2020-07-29 12:51:07 +03:00
||| List reverse applied twice yields the identity function.
export
2021-01-06 19:45:44 +03:00
reverseInvolutive : (xs : List a) -> reverse (reverse xs) = xs
reverseInvolutive [] = Refl
reverseInvolutive (x :: xs) = rewrite revOnto [x] xs in
rewrite sym (revAppend (reverse xs) [x]) in
2021-01-06 19:45:44 +03:00
cong (x ::) $ reverseInvolutive xs
2020-07-29 12:51:07 +03:00
export
dropFusion : (n, m : Nat) -> (l : List t) -> drop n (drop m l) = drop (n+m) l
dropFusion Z m l = Refl
dropFusion (S n) Z l = rewrite plusZeroRightNeutral n in Refl
dropFusion (S n) (S m) [] = Refl
dropFusion (S n) (S m) (x::l) = rewrite plusAssociative n 1 m in
rewrite plusCommutative n 1 in
dropFusion (S n) m l
[ refactor ] suggested during SPLV Main change =========== The main change is to the type of function dealing with an untouched segment of the local scope. e.g. ``` weak : {outer, vars : _} -> (ns : List Name) -> tm (outer ++ inner) -> tm (outer ++ ns ++ inner) ``` Instead we now write ``` weak : SizeOf ns -> tm (outer ++ inner) -> tm (outer ++ ns ++ inner) ``` meaning that we do not need the values of `outer`, `inner` and `ns` at runtime. Instead we only demand a `SizeOf ns` which is a `Nat` together with an (erased) proof that `ns` is of that length. Other modifications =================== Quadratic behaviour ------------------- A side effect of this refactor is the removal of two sources of quadratic behaviour. They typically arise in a situation where work is done on a scope of the form ``` outer ++ done ++ ns ++ inner ``` When `ns` is non-empty, some work is performed and then the variable is moved to the pile of things we are `done` with. This leads to recursive calls of the form `f done` -> `f (done ++ [v])` leading to a cost quadratic in the size of `ns`. Now that we only care about `SizeOf done`, the recursive call is (once all the runtime irrelevant content is erased) for the form `f n` -> `f (S n)`! More runtime irrelevance ------------------------ In some places we used to rely on a list of names `vars` being available. However once we only care about the length of `vars`, the fact it is not available is not a limitation. For instance a `SizeOf vars` can be reconstructed from an environment assigning values to `vars` even if `vars` is irrelevant. Indeed the size of the environment is the same as that of `vars`.
2020-08-26 18:02:26 +03:00
export
lengthMap : (xs : List a) -> length (map f xs) = length xs
lengthMap [] = Refl
lengthMap (x :: xs) = cong S (lengthMap xs)