Basic operations on List
. #
We define
- basic operations on
List
, - simp lemmas for applying the operations on
.nil
and.cons
arguments (in the cases where the right hand side is simple to state; otherwise these are deferred toInit.Data.List.Lemmas
), - the minimal lemmas which are required for setting up
Init.Data.Array.Basic
.
In Init.Data.List.Impl
we give tail-recursive versions of these operations
along with @[csimp]
lemmas,
In Init.Data.List.Lemmas
we develop the full API for these functions.
Recall that length
, get
, set
, foldl
, and concat
have already been defined in Init.Prelude
.
The operations are organized as follow:
- Equality:
beq
,isEqv
. - Lexicographic ordering:
lt
,le
, and instances. - Head and tail operators:
head
,head?
,headD?
,tail
,tail?
,tailD
. - Basic operations:
map
,filter
,filterMap
,foldr
,append
,join
,pure
,bind
,replicate
, andreverse
. - Additional functions defined in terms of these:
leftpad
,rightPad
, andreduceOption
. - List membership:
isEmpty
,elem
,contains
,mem
(and the∈
notation), and decidability for predicates quantifying over membership in aList
. - Sublists:
take
,drop
,takeWhile
,dropWhile
,partition
,dropLast
,isPrefixOf
,isPrefixOf?
,isSuffixOf
,isSuffixOf?
,Subset
,Sublist
,rotateLeft
androtateRight
. - Manipulating elements:
replace
,insert
,erase
,eraseP
,eraseIdx
. - Finding elements:
find?
,findSome?
,findIdx
,indexOf
,findIdx?
,indexOf?
,countP
,count
, andlookup
. - Logic:
any
,all
,or
, andand
. - Zippers:
zipWith
,zip
,zipWithAll
, andunzip
. - Ranges and enumeration:
range
,iota
,enumFrom
, andenum
. - Minima and maxima:
min?
andmax?
. - Other functions:
intersperse
,intercalate
,eraseDups
,eraseReps
,span
,groupBy
,removeAll
(currently these functions are mostly only used in meta code, and do not have API suitable for verification).
Further operations are defined in Init.Data.List.BasicAux
(because they use Array
in their implementations), namely:
- Variant getters:
get!
,get?
,getD
,getLast
,getLast!
,getLast?
, andgetLastD
. - Head and tail:
head!
,tail!
. - Other operations on sublists:
partitionMap
,rotateLeft
, androtateRight
.
Preliminaries from Init.Prelude
#
length #
set #
foldl #
concat #
Equality #
O(min |as| |bs|)
. Returns true if as
and bs
have the same length,
and they are pairwise related by eqv
.
Lexicographic ordering #
The lexicographic order on lists.
[] < a::as
, and a::as < b::bs
if a < b
or if a
and b
are equivalent and as < bs
.
- nil: ∀ {α : Type u} [inst : LT α] (b : α) (bs : List α), [].lt (b :: bs)
[]
is the smallest element in the order. - head: ∀ {α : Type u} [inst : LT α] {a : α} (as : List α) {b : α} (bs : List α), a < b → (a :: as).lt (b :: bs)
If
a < b
thena::as < b::bs
. - tail: ∀ {α : Type u} [inst : LT α] {a : α} {as : List α} {b : α} {bs : List α},
¬a < b → ¬b < a → as.lt bs → (a :: as).lt (b :: bs)
If
a
andb
are equivalent andas < bs
, thena::as < b::bs
.
Equations
- x✝.instDecidableLeOfDecidableRelLt x = inferInstanceAs (Decidable ¬x < x✝)
Alternative getters #
get? #
Returns the i
-th element in the list (zero-based).
If the index is out of bounds (i ≥ as.length
), this function returns none
.
Also see get
, getD
and get!
.
getD #
getLast #
getLast? #
getLastD #
Head and tail #
head #
head? #
headD #
tail #
tail? #
tailD #
Basic List
operations. #
We define the basic functional programming operations on List
:
map
, filter
, filterMap
, foldr
, append
, join
, pure
, bind
, replicate
, and reverse
.
map #
filter #
O(|l|)
. filter f l
returns the list of elements in l
for which f
returns true.
filter (· > 2) [1, 2, 5, 2, 7, 7] = [5, 7, 7]
Equations
- List.filter p [] = []
- List.filter p (a :: as) = match p a with | true => a :: List.filter p as | false => List.filter p as
filterMap #
O(|l|)
. filterMap f l
takes a function f : α → Option β
and applies it to each element of l
;
the resulting non-none
values are collected to form the output list.
filterMap
(fun x => if x > 2 then some (2 * x) else none)
[1, 2, 5, 2, 7, 7]
= [10, 14, 14]
Equations
- List.filterMap f [] = []
- List.filterMap f (a :: as) = match f a with | none => List.filterMap f as | some b => b :: List.filterMap f as
foldr #
O(|l|)
. Applies function f
to all of the elements of the list, from right to left.
foldr f init [a, b, c] = f a <| f b <| f c <| init
Equations
- List.foldr f init [] = init
- List.foldr f init (a :: as) = f a (List.foldr f init as)
reverse #
Auxiliary for List.reverse
. List.reverseAux l r = l.reverse ++ r
, but it is defined directly.
O(|as|)
. Reverse of a list:
[1, 2, 3, 4].reverse = [4, 3, 2, 1]
Note that because of the "functional but in place" optimization implemented by Lean's compiler, this function works without any allocations provided that the input list is unshared: it simply walks the linked list and reverses all the node pointers.
Equations
- as.reverse = as.reverseAux []
append #
Tail-recursive version of List.append
.
Most of the tail-recursive implementations are in Init.Data.List.Impl
,
but appendTR
must be set up immediately,
because otherwise Append (List α)
instance below will not use it.
Equations
- as.appendTR bs = as.reverse.reverseAux bs
Equations
- ⋯ = ⋯
Equations
- ⋯ = ⋯
join #
pure #
bind #
bind xs f
is the bind operation of the list monad. It applies f
to each element of xs
to get a list of lists, and then concatenates them all together.
Equations
Equations
replicate #
replicate n a
is n
copies of a
:
replicate 5 a = [a, a, a, a, a]
Equations
- List.replicate 0 x = []
- List.replicate n.succ x = x :: List.replicate n x
Additional functions #
leftpad and rightpad #
Pads l : List α
on the left with repeated occurrences of a : α
until it is of length n
.
If l
is initially larger than n
, just return l
.
Equations
- List.leftpad n a l = List.replicate (n - l.length) a ++ l
Pads l : List α
on the right with repeated occurrences of a : α
until it is of length n
.
If l
is initially larger than n
, just return l
.
Equations
- List.rightpad n a l = l ++ List.replicate (n - l.length) a
reduceOption #
Drop none
s from a list, and replace each remaining some a
with a
.
Equations
- List.reduceOption = List.filterMap id
List membership #
EmptyCollection #
Equations
- List.instEmptyCollection = { emptyCollection := [] }
isEmpty #
elem #
contains #
Mem #
a ∈ l
is a predicate which asserts that a
is in the list l
.
Unlike elem
, this uses =
instead of ==
and is suited for mathematical reasoning.
a ∈ [x, y, z] ↔ a = x ∨ a = y ∨ a = z
Equations
Equations
- List.decidableBEx p [] = isFalse ⋯
- List.decidableBEx p (a :: as) = if h₁ : p a then isTrue ⋯ else match List.decidableBEx p as with | isTrue h₂ => isTrue ⋯ | isFalse h₂ => isFalse ⋯
Equations
- List.decidableBAll p [] = isTrue ⋯
- List.decidableBAll p (a :: as) = if h₁ : p a then match List.decidableBAll p as with | isTrue h₂ => isTrue ⋯ | isFalse h₂ => isFalse ⋯ else isFalse ⋯
Sublists #
take #
O(min n |xs|)
. Returns the first n
elements of xs
, or the whole list if n
is too large.
drop #
O(min n |xs|)
. Removes the first n
elements of xs
.
takeWhile #
O(|xs|)
. Returns the longest initial segment of xs
for which p
returns true.
Equations
- List.takeWhile p [] = []
- List.takeWhile p (a :: as) = match p a with | true => a :: List.takeWhile p as | false => []
dropWhile #
O(|l|)
. dropWhile p l
removes elements from the list until it finds the first element
for which p
returns false; this element and everything after it is returned.
dropWhile (· < 4) [1, 3, 2, 4, 2, 7, 4] = [4, 2, 7, 4]
Equations
- List.dropWhile p [] = []
- List.dropWhile p (a :: as) = match p a with | true => List.dropWhile p as | false => a :: as
partition #
O(|l|)
. partition p l
calls p
on each element of l
, partitioning the list into two lists
(l_true, l_false)
where l_true
has the elements where p
was true
and l_false
has the elements where p
is false.
partition p l = (filter p l, filter (not ∘ p) l)
, but it is slightly more efficient
since it only has to do one pass over the list.
partition (· > 2) [1, 2, 5, 2, 7, 7] = ([5, 7, 7], [1, 2, 2])
Equations
- List.partition p as = List.partition.loop p as ([], [])
Equations
- List.partition.loop p [] (bs, cs) = (bs.reverse, cs.reverse)
- List.partition.loop p (a :: as) (bs, cs) = match p a with | true => List.partition.loop p as (a :: bs, cs) | false => List.partition.loop p as (bs, a :: cs)
dropLast #
Subset #
Equations
- x✝.instDecidableRelSubsetOfDecidableEq x = List.decidableBAll (Membership.mem x) x✝
Sublist and isSublist #
l₁ <+ l₂
, or Sublist l₁ l₂
, says that l₁
is a (non-contiguous) subsequence of l₂
.
- slnil: ∀ {α : Type u_1}, [].Sublist []
the base case:
[]
is a sublist of[]
- cons: ∀ {α : Type u_1} {l₁ l₂ : List α} (a : α), l₁.Sublist l₂ → l₁.Sublist (a :: l₂)
If
l₁
is a subsequence ofl₂
, then it is also a subsequence ofa :: l₂
. - cons₂: ∀ {α : Type u_1} {l₁ l₂ : List α} (a : α), l₁.Sublist l₂ → (a :: l₁).Sublist (a :: l₂)
If
l₁
is a subsequence ofl₂
, thena :: l₁
is a subsequence ofa :: l₂
.
l₁ <+ l₂
, or Sublist l₁ l₂
, says that l₁
is a (non-contiguous) subsequence of l₂
.
Equations
- List.«term_<+_» = Lean.ParserDescr.trailingNode `List.term_<+_ 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " <+ ") (Lean.ParserDescr.cat `term 51))
True if the first list is a potentially non-contiguous sub-sequence of the second list.
IsPrefix / isPrefixOf / isPrefixOf? #
IsPrefix l₁ l₂
, or l₁ <+: l₂
, means that l₁
is a prefix of l₂
,
that is, l₂
has the form l₁ ++ t
for some t
.
Equations
- List.«term_<+:_» = Lean.ParserDescr.trailingNode `List.term_<+:_ 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " <+: ") (Lean.ParserDescr.cat `term 51))
IsSuffix / isSuffixOf / isSuffixOf? #
isSuffixOf l₁ l₂
returns true
Iff l₁
is a suffix of l₂
.
That is, there exists a t
such that l₂ == t ++ l₁
.
Equations
- l₁.isSuffixOf l₂ = l₁.reverse.isPrefixOf l₂.reverse
isSuffixOf? l₁ l₂
returns some t
when l₂ == t ++ l₁
.
Equations
- l₁.isSuffixOf? l₂ = Option.map List.reverse (l₁.reverse.isPrefixOf? l₂.reverse)
IsSuffix l₁ l₂
, or l₁ <:+ l₂
, means that l₁
is a suffix of l₂
,
that is, l₂
has the form t ++ l₁
for some t
.
Equations
- List.«term_<:+_» = Lean.ParserDescr.trailingNode `List.term_<:+_ 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " <:+ ") (Lean.ParserDescr.cat `term 51))
IsInfix #
IsInfix l₁ l₂
, or l₁ <:+: l₂
, means that l₁
is a contiguous
substring of l₂
, that is, l₂
has the form s ++ l₁ ++ t
for some s, t
.
Equations
- List.«term_<:+:_» = Lean.ParserDescr.trailingNode `List.term_<:+:_ 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " <:+: ") (Lean.ParserDescr.cat `term 51))
splitAt #
Split a list at an index.
splitAt 2 [a, b, c] = ([a, b], [c])
Equations
- List.splitAt n l = List.splitAt.go l l n []
Auxiliary for splitAt
:
splitAt.go l xs n acc = (acc.reverse ++ take n xs, drop n xs)
if n < xs.length
,
and (l, [])
otherwise.
Equations
- List.splitAt.go l [] x✝ x = (l, [])
- List.splitAt.go l (x_3 :: xs) n.succ x = List.splitAt.go l xs n (x_3 :: x)
- List.splitAt.go l x✝¹ x✝ x = (x.reverse, x✝¹)
rotateLeft #
O(n)
. Rotates the elements of xs
to the left such that the element at
xs[i]
rotates to xs[(i - n) % l.length]
.
rotateLeft [1, 2, 3, 4, 5] 3 = [4, 5, 1, 2, 3]
rotateLeft [1, 2, 3, 4, 5] 5 = [1, 2, 3, 4, 5]
rotateLeft [1, 2, 3, 4, 5] = [2, 3, 4, 5, 1]
rotateRight #
O(n)
. Rotates the elements of xs
to the right such that the element at
xs[i]
rotates to xs[(i + n) % l.length]
.
rotateRight [1, 2, 3, 4, 5] 3 = [3, 4, 5, 1, 2]
rotateRight [1, 2, 3, 4, 5] 5 = [1, 2, 3, 4, 5]
rotateRight [1, 2, 3, 4, 5] = [5, 1, 2, 3, 4]
Pairwise, Nodup #
Pairwise R l
means that all the elements with earlier indexes are
R
-related to all the elements with later indexes.
Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3
For example if R = (·≠·)
then it asserts l
has no duplicates,
and if R = (·<·)
then it asserts that l
is (strictly) sorted.
- nil: ∀ {α : Type u} {R : α → α → Prop}, List.Pairwise R []
All elements of the empty list are vacuously pairwise related.
- cons: ∀ {α : Type u} {R : α → α → Prop} {a : α} {l : List α}, (∀ (a' : α), a' ∈ l → R a a') → List.Pairwise R l → List.Pairwise R (a :: l)
Instances For
Nodup l
means that l
has no duplicates, that is, any element appears at most
once in the List. It is defined as Pairwise (≠)
.
Equations
- List.Nodup = List.Pairwise fun (x1 x2 : α) => x1 ≠ x2
Instances For
Equations
- List.nodupDecidable = List.instDecidablePairwise
Manipulating elements #
replace #
O(|l|)
. replace l a b
replaces the first element in the list equal to a
with b
.
insert #
erase #
O(|l|)
. erase l a
removes the first occurrence of a
from l
.
eraseP p l
removes the first element of l
satisfying the predicate p
.
Equations
- List.eraseP p [] = []
- List.eraseP p (a :: as) = bif p a then as else a :: List.eraseP p as
eraseIdx #
O(i)
. eraseIdx l i
removes the i
'th element of the list l
.
Finding elements
find? #
O(|l|)
. find? p l
returns the first element for which p
returns true,
or none
if no such element is found.
Equations
- List.find? p [] = none
- List.find? p (a :: as) = match p a with | true => some a | false => List.find? p as
findSome? #
O(|l|)
. findSome? f l
applies f
to each element of l
, and returns the first non-none
result.
findSome? (fun x => if x < 5 then some (10 * x) else none) [7, 6, 5, 8, 1, 2, 6] = some 10
Equations
- List.findSome? f [] = none
- List.findSome? f (a :: as) = match f a with | some b => some b | none => List.findSome? f as
findIdx #
Returns the index of the first element satisfying p
, or the length of the list otherwise.
Equations
- List.findIdx p l = List.findIdx.go p l 0
Auxiliary for findIdx
: findIdx.go p l n = findIdx p l + n
Equations
- List.findIdx.go p [] x = x
- List.findIdx.go p (a :: l) x = bif p a then x else List.findIdx.go p l (x + 1)
indexOf #
Returns the index of the first element equal to a
, or the length of the list otherwise.
Equations
- List.indexOf a = List.findIdx fun (x : α) => x == a
findIdx? #
Return the index of the first occurrence of an element satisfying p
.
Equations
- List.findIdx? p [] x = none
- List.findIdx? p (a :: l) x = if p a = true then some x else List.findIdx? p l (x + 1)
indexOf? #
Return the index of the first occurrence of a
in the list.
Equations
- List.indexOf? a✝ a = List.findIdx? (fun (x : α) => x == a✝) a
countP #
countP p l
is the number of elements of l
that satisfy p
.
Equations
- List.countP p l = List.countP.go p l 0
Auxiliary for countP
: countP.go p l acc = countP p l + acc
.
Equations
- List.countP.go p [] x = x
- List.countP.go p (a :: l) x = bif p a then List.countP.go p l (x + 1) else List.countP.go p l x
count #
count a l
is the number of occurrences of a
in l
.
Equations
- List.count a = List.countP fun (x : α) => x == a
lookup #
O(|l|)
. lookup a l
treats l : List (α × β)
like an association list,
and returns the first β
value corresponding to an α
value in the list equal to a
.
Equations
- List.lookup x [] = none
- List.lookup x ((k, b) :: es) = match x == k with | true => some b | false => List.lookup x es
Permutations #
Perm #
Perm l₁ l₂
or l₁ ~ l₂
asserts that l₁
and l₂
are permutations
of each other. This is defined by induction using pairwise swaps.
- nil: ∀ {α : Type u}, [].Perm []
[] ~ []
- cons: ∀ {α : Type u} (x : α) {l₁ l₂ : List α}, l₁.Perm l₂ → (x :: l₁).Perm (x :: l₂)
l₁ ~ l₂ → x::l₁ ~ x::l₂
- swap: ∀ {α : Type u} (x y : α) (l : List α), (y :: x :: l).Perm (x :: y :: l)
x::y::l ~ y::x::l
- trans: ∀ {α : Type u} {l₁ l₂ l₃ : List α}, l₁.Perm l₂ → l₂.Perm l₃ → l₁.Perm l₃
Perm
is transitive.
Instances For
Perm l₁ l₂
or l₁ ~ l₂
asserts that l₁
and l₂
are permutations
of each other. This is defined by induction using pairwise swaps.
Equations
- List.«term_~_» = Lean.ParserDescr.trailingNode `List.term_~_ 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " ~ ") (Lean.ParserDescr.cat `term 51))
isPerm #
Logical operations #
any #
all #
or #
and #
Zippers #
zipWith #
O(min |xs| |ys|)
. Applies f
to the two lists in parallel, stopping at the shorter list.
zipWith f [x₁, x₂, x₃] [y₁, y₂, y₃, y₄] = [f x₁ y₁, f x₂ y₂, f x₃ y₃]
Equations
- List.zipWith f (x_2 :: xs) (y :: ys) = f x_2 y :: List.zipWith f xs ys
- List.zipWith f x✝ x = []
zip #
O(min |xs| |ys|)
. Combines the two lists into a list of pairs, with one element from each list.
The longer list is truncated to match the shorter list.
zip [x₁, x₂, x₃] [y₁, y₂, y₃, y₄] = [(x₁, y₁), (x₂, y₂), (x₃, y₃)]
Equations
- List.zip = List.zipWith Prod.mk
zipWithAll #
O(max |xs| |ys|)
.
Version of List.zipWith
that continues to the end of both lists,
passing none
to one argument once the shorter list has run out.
Equations
- List.zipWithAll f [] x = List.map (fun (b : β) => f none (some b)) x
- List.zipWithAll f (a :: as) [] = List.map (fun (a : α) => f (some a) none) (a :: as)
- List.zipWithAll f (a :: as) (b :: bs) = f (some a) (some b) :: List.zipWithAll f as bs
unzip #
O(|l|)
. Separates a list of pairs into two lists containing the first components and second components.
unzip [(x₁, y₁), (x₂, y₂), (x₃, y₃)] = ([x₁, x₂, x₃], [y₁, y₂, y₃])
Ranges and enumeration #
range #
O(n)
. range n
is the numbers from 0
to n
exclusive, in increasing order.
range 5 = [0, 1, 2, 3, 4]
Equations
- List.range n = List.range.loop n []
Equations
- List.range.loop 0 x = x
- List.range.loop n.succ x = List.range.loop n (n :: x)
range' #
range' start len step
is the list of numbers [start, start+step, ..., start+(len-1)*step]
.
It is intended mainly for proving properties of range
and iota
.
Equations
- List.range' x✝ 0 x = []
- List.range' x✝ n.succ x = x✝ :: List.range' (x✝ + x) n x
iota #
enumFrom #
O(|l|)
. enumFrom n l
is like enum
but it allows you to specify the initial index.
enumFrom 5 [a, b, c] = [(5, a), (6, b), (7, c)]
Equations
- List.enumFrom x [] = []
- List.enumFrom x (x_2 :: xs) = (x, x_2) :: List.enumFrom (x + 1) xs
enum #
Minima and maxima #
min? #
max? #
Other list operations #
The functions are currently mostly used in meta code, and do not have sufficient API developed for verification work.
intersperse #
O(|l|)
. intersperse sep l
alternates sep
and the elements of l
:
intersperse sep [] = []
intersperse sep [a] = [a]
intersperse sep [a, b] = [a, sep, b]
intersperse sep [a, b, c] = [a, sep, b, sep, c]
Equations
- List.intersperse sep [] = []
- List.intersperse sep [head] = [head]
- List.intersperse sep (a :: as) = a :: sep :: List.intersperse sep as
intercalate #
O(|xs|)
. intercalate sep xs
alternates sep
and the elements of xs
:
intercalate sep [] = []
intercalate sep [a] = a
intercalate sep [a, b] = a ++ sep ++ b
intercalate sep [a, b, c] = a ++ sep ++ b ++ sep ++ c
Equations
- sep.intercalate xs = (List.intersperse sep xs).join
eraseDups #
O(|l|^2)
. Erase duplicated elements in the list.
Keeps the first occurrence of duplicated elements.
eraseDups [1, 3, 2, 2, 3, 5] = [1, 3, 2, 5]
Equations
- as.eraseDups = List.eraseDups.loop as []
Equations
- List.eraseDups.loop [] x = x.reverse
- List.eraseDups.loop (a :: l) x = match List.elem a x with | true => List.eraseDups.loop l x | false => List.eraseDups.loop l (a :: x)
eraseReps #
Equations
- List.eraseReps.loop x✝ [] x = (x✝ :: x).reverse
- List.eraseReps.loop x✝ (a' :: as) x = match x✝ == a' with | true => List.eraseReps.loop x✝ as x | false => List.eraseReps.loop a' as (x✝ :: x)
span #
O(|l|)
. span p l
splits the list l
into two parts, where the first part
contains the longest initial segment for which p
returns true
and the second part is everything else.
span (· > 5) [6, 8, 9, 5, 2, 9] = ([6, 8, 9], [5, 2, 9])
span (· > 10) [6, 8, 9, 5, 2, 9] = ([], [6, 8, 9, 5, 2, 9])
Equations
- List.span p as = List.span.loop p as []
Equations
- List.span.loop p [] x = (x.reverse, [])
- List.span.loop p (a :: l) x = match p a with | true => List.span.loop p l (a :: x) | false => (x.reverse, a :: l)
groupBy #
O(|l|)
. groupBy R l
splits l
into chains of elements
such that adjacent elements are related by R
.
groupBy (·==·) [1, 1, 2, 2, 2, 3, 2] = [[1, 1], [2, 2, 2], [3], [2]]
groupBy (·<·) [1, 2, 5, 4, 5, 1, 4] = [[1, 2, 5], [4, 5], [1, 4]]
Equations
- List.groupBy R [] = []
- List.groupBy R (a :: as) = List.groupBy.loop R as a [] []
The arguments of groupBy.loop l ag g gs
represent the following:
l : List α
are the elements which we still need to group.ag : α
is the previous element for which a comparison was performed.g : List α
is the group currently being assembled, in reverse order.gs : List (List α)
is all of the groups that have been completed, in reverse order.
Equations
- List.groupBy.loop R (a :: as) x✝¹ x✝ x = match R x✝¹ a with | true => List.groupBy.loop R as a (x✝¹ :: x✝) x | false => List.groupBy.loop R as a [] ((x✝¹ :: x✝).reverse :: x)
- List.groupBy.loop R [] x✝¹ x✝ x = ((x✝¹ :: x✝).reverse :: x).reverse
removeAll #
O(|xs|)
. Computes the "set difference" of lists,
by filtering out all elements of xs
which are also in ys
.
removeAll [1, 1, 5, 1, 2, 4, 5] [1, 2, 2] = [5, 4, 5]
Equations
- xs.removeAll ys = List.filter (fun (x : α) => !List.elem x ys) xs
Runtime re-implementations using @[csimp]
#
More of these re-implementations are provided in Init/Data/List/Impl.lean
.
They can not be here, because the remaining ones required Array
for their implementation.
This leaves a dangerous situation: if you import this file, but not Init/Data/List/Impl.lean
,
then at runtime you will get non tail-recursive versions.
length #
map #
Equations
- List.mapTR.loop f [] x = x.reverse
- List.mapTR.loop f (a :: as) x = List.mapTR.loop f as (f a :: x)
filter #
Tail-recursive version of List.filter
.
Equations
- List.filterTR p as = List.filterTR.loop p as []
Equations
- List.filterTR.loop p [] x = x.reverse
- List.filterTR.loop p (a :: l) x = match p a with | true => List.filterTR.loop p l (a :: x) | false => List.filterTR.loop p l x
replicate #
Tail-recursive version of List.replicate
.
Equations
- List.replicateTR n a = List.replicateTR.loop a n []
Equations
- List.replicateTR.loop a 0 x = x
- List.replicateTR.loop a n.succ x = List.replicateTR.loop a n (a :: x)
Additional functions #
leftpad #
Optimized version of leftpad
.
Equations
- List.leftpadTR n a l = List.replicateTR.loop a (n - l.length) l
Zippers #
unzip #
Ranges and enumeration #
range' #
Optimized version of range'
.
Equations
- List.range'TR s n step = List.range'TR.go step n (s + step * n) []
Auxiliary for range'TR
: range'TR.go n e = [e-n, ..., e-1] ++ acc
.
Equations
- List.range'TR.go step 0 x✝ x = x
- List.range'TR.go step n.succ x✝ x = List.range'TR.go step n (x✝ - step) ((x✝ - step) :: x)
iota #
Tail-recursive version of List.iota
.
Equations
- List.iotaTR n = List.iotaTR.go n []
Equations
- List.iotaTR.go 0 x = x.reverse
- List.iotaTR.go n.succ x = List.iotaTR.go n (n.succ :: x)
Other list operations #
intersperse #
Tail recursive version of List.intersperse
.
Equations
- List.intersperseTR sep [] = []
- List.intersperseTR sep [x_1] = [x_1]
- List.intersperseTR sep (x_1 :: y :: xs) = x_1 :: sep :: y :: List.foldr (fun (a : α) (r : List α) => sep :: a :: r) [] xs