doc: review List docstrings for manual (#7452)

This PR makes the style of all `List` docstrings that appear in the
language reference consistent.

Relies on #7240 for links and example formatting.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
This commit is contained in:
David Thrane Christiansen 2025-03-13 17:10:06 +01:00 committed by GitHub
parent 06c57826ae
commit 25179352b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1616 additions and 460 deletions

View file

@ -337,6 +337,9 @@ def prevn : Iterator → Nat → Iterator
end Iterator
end ByteArray
/--
Converts a list of bytes into a `ByteArray`.
-/
def List.toByteArray (bs : List UInt8) : ByteArray :=
let rec loop
| [], r => r

View file

@ -167,6 +167,9 @@ def foldl {β : Type v} (f : β → Float → β) (init : β) (as : FloatArray)
end FloatArray
/--
Converts a list of floats into a `FloatArray`.
-/
def List.toFloatArray (ds : List Float) : FloatArray :=
let rec loop
| [], r => r

View file

@ -13,29 +13,44 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/-- `O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
but is defined only when all members of `l` satisfy `P`, using the proof
to apply `f`. -/
/--
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over
a list `l : List α`, given a proof that every element of `l` in fact satisfies `P`.
`O(|l|)`. `List.pmap`, named for “partial map,” is the equivalent of `List.map` for such partial
functions.
-/
def pmap {P : α → Prop} (f : ∀ a, P a → β) : ∀ l : List α, (H : ∀ a ∈ l, P a) → List β
| [], _ => []
| a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
Unsafe implementation of `attachWith` that takes advantage of the fact that the representation of
`List {x // P x}` is the same as the input `List α`.
(Someday, the compiler might do this optimization automatically, but until then...)
-/
@[inline] private unsafe def attachWithImpl
(l : List α) (P : α → Prop) (_ : ∀ x ∈ l, P x) : List {x // P x} := unsafeCast l
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `l` to produce a new list
with the same elements but in the type `{x // P x}`. -/
/--
“Attaches” individual proofs to a list of values that satisfy a predicate `P`, returning a list of
elements in the corresponding subtype `{ x // P x }`.
`O(1)`.
-/
@[implemented_by attachWithImpl] def attachWith
(l : List α) (P : α → Prop) (H : ∀ x ∈ l, P x) : List {x // P x} := pmap Subtype.mk l H
/-- `O(1)`. "Attach" the proof that the elements of `l` are in `l` to produce a new list
with the same elements but in the type `{x // x ∈ l}`. -/
/--
"Attaches" the proof that the elements of `l` are in fact elements of `l`, producing a new list with
the same elements but in the subtype `{ x // x ∈ l }`.
`O(1)`.
This function is primarily used to allow definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
`List.map`) to prove that an value taken from a list is smaller than the list. This allows the
well-founded recursion mechanism to prove that the function terminates.
-/
@[inline] def attach (l : List α) : List {x // x ∈ l} := attachWith l _ fun _ => id
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
@ -650,11 +665,19 @@ Further, we provide simp lemmas that push `unattach` inwards.
-/
/--
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
It is introduced as an intermediate step by lemmas such as `map_subtype`,
and is ideally subsequently simplified away by `unattach_attach`.
Maps a list of terms in a subtype to the corresponding terms in the type by forgetting that they
satisfy the predicate.
If not, usually the right approach is `simp [List.unattach, -List.map_subtype]` to unfold.
This is the inverse of `List.attachWith` and a synonym for `l.map (·.val)`.
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
This function is usually inserted automatically by Lean as an intermediate step while proving
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
the elaboration of definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
state, the right approach is usually the tactic `simp [List.unattach, -List.map_subtype]`.
-/
def unattach {α : Type _} {p : α → Prop} (l : List { x // p x }) : List α := l.map (·.val)

File diff suppressed because it is too large Load diff

View file

@ -79,10 +79,16 @@ theorem get!_cons_zero [Inhabited α] (l : List α) (a : α) : (a::l).get! 0 = a
/-! ### getD -/
/--
Returns the `i`-th element in the list (zero-based).
Returns the element at the provided index, counting from `0`. Returns `fallback` if the index is out
of bounds.
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
See also `get?` and `get!`.
To return an `Option` depending on whether the index is in bounds, use `as[i]?`. To panic if the
index is out of bounds, use `as[i]!`.
Examples:
* `["spring", "summer", "fall", "winter"].getD 2 "never" = "fall"`
* `["spring", "summer", "fall", "winter"].getD 0 "never" = "spring"`
* `["spring", "summer", "fall", "winter"].getD 4 "never" = "never"`
-/
def getD (as : List α) (i : Nat) (fallback : α) : α :=
as[i]?.getD fallback
@ -92,10 +98,16 @@ def getD (as : List α) (i : Nat) (fallback : α) : α :=
/-! ### getLast! -/
/--
Returns the last element in the list.
Returns the last element in the list. Panics and returns `default` if the list is empty.
If the list is empty, this function panics when executed, and returns `default`.
See `getLast` and `getLastD` for safer alternatives.
Safer alternatives include:
* `getLast?`, which returns an `Option`,
* `getLastD`, which takes a fallback value for empty lists, and
* `getLast`, which requires a proof that the list is non-empty.
Examples:
* `["circle", "rectangle"].getLast! = "rectangle"`
* `["circle"].getLast! = "circle"`
-/
def getLast! [Inhabited α] : List αα
| [] => panic! "empty list"
@ -106,10 +118,12 @@ def getLast! [Inhabited α] : List αα
/-! ### head! -/
/--
Returns the first element in the list.
Returns the first element in the list. If the list is empty, panics and returns `default`.
If the list is empty, this function panics when executed, and returns `default`.
See `head` and `headD` for safer alternatives.
Safer alternatives include:
* `List.head`, which requires a proof that the list is non-empty,
* `List.head?`, which returns an `Option`, and
* `List.headD`, which returns an explicitly-provided fallback value on empty lists.
-/
def head! [Inhabited α] : List αα
| [] => panic! "empty list"
@ -118,10 +132,17 @@ def head! [Inhabited α] : List αα
/-! ### tail! -/
/--
Drops the first element of the list.
Drops the first element of a nonempty list, returning the tail. If the list is empty, this function
panics when executed and returns the empty list.
If the list is empty, this function panics when executed, and returns the empty list.
See `tail` and `tailD` for safer alternatives.
Safer alternatives include
* `tail`, which returns the empty list without panicking,
* `tail?`, which returns an `Option`, and
* `tailD`, which returns a fallback value when passed the empty list.
Examples:
* `["apple", "banana", "grape"].tail! = ["banana", "grape"]`
* `["banana", "grape"].tail! = ["grape"]`
-/
def tail! : List α → List α
| [] => panic! "empty list"
@ -132,17 +153,30 @@ def tail! : List α → List α
/-! ### partitionM -/
/--
Monadic generalization of `List.partition`.
Returns a pair of lists that together contain all the elements of `as`. The first list contains
those elements for which the monadic predicate `p` returns `true`, and the second contains those for
which `p` returns `false`. The list's elements are examined in order, from left to right.
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic` or `Init.Data.List.Control`.
```
This is a monadic version of `List.partition`.
Example:
```lean example
def posOrNeg (x : Int) : Except String Bool :=
if x > 0 then pure true
else if x < 0 then pure false
else throw "Zero is not positive or negative"
partitionM posOrNeg [-1, 2, 3] = Except.ok ([2, 3], [-1])
partitionM posOrNeg [0, 2, 3] = Except.error "Zero is not positive or negative"
```
```lean example
#eval [-1, 2, 3].partitionM posOrNeg
```
```output
Except.ok ([2, 3], [-1])
```
```lean example
#eval [0, 2, 3].partitionM posOrNeg
```
```output
Except.error "Zero is not positive or negative"
```
-/
@[inline] def partitionM [Monad m] (p : α → m Bool) (l : List α) : m (List α × List α) :=
@ -162,12 +196,12 @@ where
/-! ### partitionMap -/
/--
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
whilst partitioning the result into a pair of lists, `List β × List γ`,
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
```
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
```
Applies a function that returns a disjoint union to each element of a list, collecting the `Sum.inl`
and `Sum.inr` results into separate lists.
Examples:
* `[0, 1, 2, 3].partitionMap (fun x => if x % 2 = 0 then .inl x else .inr x) = ([0, 2], [1, 3])`
* `[0, 1, 2, 3].partitionMap (fun x => if x = 0 then .inl x else .inr x) = ([0], [1, 2, 3])`
-/
@[inline] def partitionMap (f : α → β ⊕ γ) (l : List α) : List β × List γ := go l #[] #[] where
/-- Auxiliary for `partitionMap`:
@ -199,14 +233,24 @@ For verification purposes, `List.mapMono = List.map`.
return b' :: bs'
/--
Monomorphic `List.mapM`. The internal implementation uses pointer equality, and does not allocate a new list
if the result of each `f a` is a pointer equal value `a`.
Applies a monadic function to each element of a list, returning the list of results. The function is
monomorphic: it is required to return a value of the same type. The internal implementation uses
pointer equality, and does not allocate a new list if the result of each function call is
pointer-equal to its argument.
-/
@[implemented_by mapMonoMImp] def mapMonoM [Monad m] (as : List α) (f : α → m α) : m (List α) :=
match as with
| [] => return []
| a :: as => return (← f a) :: (← mapMonoM as f)
/--
Applies a function to each element of a list, returning the list of results. The function is
monomorphic: it is required to return a value of the same type. The internal implementation uses
pointer equality, and does not allocate a new list if the result of each function call is
pointer-equal to its argument.
For verification purposes, `List.mapMono = List.map`.
-/
def mapMono (as : List α) (f : αα) : List α :=
Id.run <| as.mapMonoM f

View file

@ -49,8 +49,9 @@ Users that want to use `mapM` with `Applicative` should use `mapA` instead.
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
results.
See `List.forM` for the variant that discards the results.
See `List.mapA` for the variant that works with `Applicative`.
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
more convenient to reason about. `List.forM` is the variant that discards the results and
`List.mapA` is the variant that works with `Applicative`.
-/
@[inline]
def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m β) (as : List α) : m (List β) :=
@ -60,15 +61,15 @@ def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α
loop as []
/--
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list of
results.
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list
of results.
NB: If `m` is also a `Monad`, then using `mapM` can be more efficient.
If `m` is also a `Monad`, then using `mapM` can be more efficient.
See `List.forA` for the variant that discards the results.
See `List.mapM` for the variant that works with `Monad`.
See `List.forA` for the variant that discards the results. See `List.mapM` for the variant that
works with `Monad`.
**Warning**: this function is not tail-recursive, meaning that it may fail with a stack overflow on long lists.
This function is not tail-recursive, so it may fail with a stack overflow on long lists.
-/
@[specialize]
def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m (List β)
@ -76,10 +77,10 @@ def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f
| a::as => List.cons <$> f a <*> mapA f as
/--
Applies the monadic action `f` on every element in the list, left-to-right.
Applies the monadic action `f` to every element in the list, in order.
See `List.mapM` for the variant that collects results.
See `List.forA` for the variant that works with `Applicative`.
`List.mapM` is a variant that collects results. `List.forA` is a variant that works on any
`Applicative`.
-/
@[specialize]
protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
@ -88,12 +89,11 @@ protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α
| a :: as => do f a; List.forM as f
/--
Applies the applicative action `f` on every element in the list, left-to-right.
Applies the applicative action `f` to every element in the list, in order.
NB: If `m` is also a `Monad`, then using `forM` can be more efficient.
If `m` is also a `Monad`, then using `List.forM` can be more efficient.
See `List.mapA` for the variant that collects results.
See `List.forM` for the variant that works with `Monad`.
`List.mapA` is a variant that collects results.
-/
@[specialize]
def forA {m : Type u → Type v} [Applicative m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
@ -110,8 +110,28 @@ def filterAuxM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) :
filterAuxM f t (cond b (h :: acc) acc)
/--
Applies the monadic predicate `p` on every element in the list, left-to-right, and returns those
elements `x` for which `p x` returns `true`.
Applies the monadic predicate `p` to every element in the list, in order from left to right, and
returns the list of elements for which `p` returns `true`.
`O(|l|)`.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterM fun x => do
IO.println s!"Checking {x}"
return x < 3
```
```output
Checking 1
Checking 2
Checking 5
Checking 2
Checking 7
Checking 7
```
```output
[1, 2, 2]
```
-/
@[inline]
def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) := do
@ -119,16 +139,56 @@ def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as
pure as.reverse
/--
Applies the monadic predicate `p` on every element in the list, right-to-left, and returns those
elements `x` for which `p x` returns `true`.
Applies the monadic predicate `p` on every element in the list in reverse order, from right to left,
and returns those elements for which `p` returns `true`. The elements of the returned list are in
the same order as in the input list.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterRevM fun x => do
IO.println s!"Checking {x}"
return x < 3
```
```output
Checking 7
Checking 7
Checking 2
Checking 5
Checking 2
Checking 1
```
```output
[1, 2, 2]
```
-/
@[inline]
def filterRevM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) :=
filterAuxM p as.reverse []
/--
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns those
results `y` for which `f x` returns `some y`.
Applies a monadic function that returns an `Option` to each element of a list, collecting the
non-`none` values.
`O(|l|)`.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterMapM fun x => do
IO.println s!"Examining {x}"
if x > 2 then return some (2 * x)
else return none
```
```output
Examining 1
Examining 2
Examining 5
Examining 2
Examining 7
Examining 7
```
```output
[10, 14, 14]
```
-/
@[inline]
def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (Option β)) (as : List α) : m (List β) :=
@ -141,8 +201,8 @@ def filterMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f
loop as []
/--
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns the
concatenation of the results.
Applies a monadic function that returns a list to each element of a list, from left to right, and
concatenates the resulting lists.
-/
@[inline]
def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (List β)) (as : List α) : m (List β) :=
@ -153,14 +213,20 @@ def flatMapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f :
loop as (bs' :: bs)
loop as []
/--
Folds a monadic function over a list from left to right:
```
foldlM f x₀ [a, b, c] = do
let x₁ ← f x₀ a
let x₂ ← f x₁ b
let x₃ ← f x₂ c
pure x₃
Folds a monadic function over a list from the left, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in order, using `f`.
Example:
```lean example
example [Monad m] (f : α → β → m α) :
List.foldlM (m := m) f x₀ [a, b, c] = (do
let x₁ ← f x₀ a
let x₂ ← f x₁ b
let x₃ ← f x₂ c
pure x₃)
:= by rfl
```
-/
@[specialize]
@ -176,13 +242,18 @@ def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s
simp [List.foldlM]
/--
Folds a monadic function over a list from right to left:
```
foldrM f x₀ [a, b, c] = do
let x₁ ← f c x₀
let x₂ ← f b x₁
let x₃ ← f a x₂
pure x₃
Folds a monadic function over a list from the right, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in order, using `f`.
Example:
```lean example
example [Monad m] (f : α → β → m β) :
List.foldrM (m := m) f x₀ [a, b, c] = (do
let x₁ ← f c x₀
let x₂ ← f b x₁
let x₃ ← f a x₂
pure x₃)
:= by rfl
```
-/
@[inline]
@ -192,32 +263,70 @@ def foldrM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} (f : α
@[simp] theorem foldrM_nil [Monad m] (f : α → β → m β) (b) : [].foldrM f b = pure b := rfl
/--
Maps `f` over the list and collects the results with `<|>`.
```
firstM f [a, b, c] = f a <|> f b <|> f c <|> failure
```
Maps `f` over the list and collects the results with `<|>`. The result for the end of the list is
`failure`.
Examples:
* `[[], [1, 2], [], [2]].firstM List.head? = some 1`
* `[[], [], []].firstM List.head? = none`
* `[].firstM List.head? = none`
-/
@[specialize]
def firstM {m : Type u → Type v} [Alternative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m β
| [] => failure
| a::as => f a <|> firstM f as
/--
Returns true if the monadic predicate `p` returns `true` for any element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `true`. The elements in `l` are examined in
order from left to right.
-/
@[specialize]
def anyM {m : Type → Type u} [Monad m] {α : Type v} (f : α → m Bool) : List α → m Bool
def anyM {m : Type → Type u} [Monad m] {α : Type v} (p : α → m Bool) : (l : List α) → m Bool
| [] => pure false
| a::as => do
match (← f a) with
match (← p a) with
| true => pure true
| false => anyM f as
| false => anyM p as
/--
Returns true if the monadic predicate `p` returns `true` for every element of `l`.
`O(|l|)`. Short-circuits upon encountering the first `false`. The elements in `l` are examined in
order from left to right.
-/
@[specialize]
def allM {m : Type → Type u} [Monad m] {α : Type v} (f : α → m Bool) : List α → m Bool
def allM {m : Type → Type u} [Monad m] {α : Type v} (p : α → m Bool) : (l : List α) → m Bool
| [] => pure true
| a::as => do
match (← f a) with
| true => allM f as
match (← p a) with
| true => allM p as
| false => pure false
/--
Returns the first element of the list for which the monadic predicate `p` returns `true`, or `none`
if no such element is found. Elements of the list are checked in order.
`O(|l|)`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].findM? fun i => do
if i < 5 then
return true
if i ≤ 6 then
IO.println s!"Almost! {i}"
return false
```
```output
Almost! 6
Almost! 5
```
```output
some 1
```
-/
@[specialize]
def findM? {m : Type → Type u} [Monad m] {α : Type} (p : α → m Bool) : List α → m (Option α)
| [] => pure none
@ -241,6 +350,29 @@ theorem findM?_pure {m} [Monad m] [LawfulMonad m] (p : α → Bool) (as : List
theorem findM?_id (p : α → Bool) (as : List α) : findM? (m := Id) p as = as.find? p :=
findM?_pure _ _
/--
Returns the first non-`none` result of applying the monadic function `f` to each element of the
list, in order. Returns `none` if `f` returns `none` for all elements.
`O(|l|)`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].findSomeM? fun i => do
if i < 5 then
return some (i * 10)
if i ≤ 6 then
IO.println s!"Almost! {i}"
return none
```
```output
Almost! 6
Almost! 5
```
```output
some 10
```
-/
@[specialize]
def findSomeM? {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m (Option β)) : List α → m (Option β)
| [] => pure none

View file

@ -11,7 +11,13 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/-- `finRange n` lists all elements of `Fin n` in order -/
/--
Lists all elements of `Fin n` in order, starting at `0`.
Examples:
* `List.finRange 0 = ([] : List Fin 0)`
* `List.finRange 2 = ([0, 1] : List Fin 2)`
-/
def finRange (n : Nat) : List (Fin n) := ofFn fun i => i
@[simp] theorem length_finRange (n) : (List.finRange n).length = n := by

View file

@ -49,7 +49,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### set -/
/-- Tail recursive version of `List.set`. -/
/--
Replaces the value at (zero-based) index `n` in `l` with `a`. If the index is out of bounds, then
the list is returned unmodified.
This is a tail-recursive version of `List.set` that's used at runtime.
Examples:
* `["water", "coffee", "soda", "juice"].set 1 "tea" = ["water", "tea", "soda", "juice"]`
* `["water", "coffee", "soda", "juice"].set 4 "tea" = ["water", "coffee", "soda", "juice"]`
-/
@[inline] def setTR (l : List α) (n : Nat) (a : α) : List α := go l n #[] where
/-- Auxiliary for `setTR`: `setTR.go l a xs n acc = acc.toList ++ set xs a`,
unless `n ≥ l.length` in which case it returns `l` -/
@ -69,7 +78,22 @@ The following operations are given `@[csimp]` replacements below:
/-! ### filterMap -/
/-- Tail recursive version of `filterMap`. -/
/--
Applies a function that returns an `Option` to each element of a list, collecting the non-`none`
values.
`O(|l|)`. This is a tail-recursive version of `List.filterMap`, used at runtime.
Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterMapTR fun x =>
if x > 2 then some (2 * x) else none
```
```output
[10, 14, 14]
```
-/
@[inline] def filterMapTR (f : α → Option β) (l : List α) : List β := go l #[] where
/-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/
@[specialize] go : List α → Array β → List β
@ -90,7 +114,17 @@ The following operations are given `@[csimp]` replacements below:
/-! ### foldr -/
/-- Tail recursive version of `List.foldr`. -/
/--
Folds a function over a list from the right, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in reverse order, using `f`.
`O(|l|)`. This is the tail-recursive replacement for `List.foldr` in runtime code.
Examples:
* `[a, b, c].foldrTR f init = f a (f b (f c init))`
* `[1, 2, 3].foldrTR (toString · ++ ·) "" = "123"`
* `[1, 2, 3].foldrTR (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
-/
@[specialize] def foldrTR (f : α → β → β) (init : β) (l : List α) : β := l.toArray.foldr f init
@[csimp] theorem foldr_eq_foldrTR : @foldr = @foldrTR := by
@ -98,7 +132,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### flatMap -/
/-- Tail recursive version of `List.flatMap`. -/
/--
Applies a function that returns a list to each element of a list, and concatenates the resulting
lists.
This is the tail-recursive version of `List.flatMap` that's used at runtime.
Examples:
* `[2, 3, 2].flatMapTR List.range = [0, 1, 0, 1, 2, 0, 1]`
* `["red", "blue"].flatMapTR String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
-/
@[inline] def flatMapTR (f : α → List β) (as : List α) : List β := go as #[] where
/-- Auxiliary for `flatMap`: `flatMap.go f as = acc.toList ++ bind f as` -/
@[specialize] go : List α → Array β → List β
@ -114,7 +157,15 @@ The following operations are given `@[csimp]` replacements below:
/-! ### flatten -/
/-- Tail recursive version of `List.flatten`. -/
/--
Concatenates a list of lists into a single list, preserving the order of the elements.
`O(|flatten L|)`. This is a tail-recursive version of `List.flatten`, used in runtime code.
Examples:
* `[["a"], ["b", "c"]].flattenTR = ["a", "b", "c"]`
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flattenTR = ["a", "b", "c", "d", "e", "f"]`
-/
@[inline] def flattenTR (l : List (List α)) : List α := l.flatMapTR id
@[csimp] theorem flatten_eq_flattenTR : @flatten = @flattenTR := by
@ -124,7 +175,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### take -/
/-- Tail recursive version of `List.take`. -/
/--
Extracts the first `n` elements of `xs`, or the whole list if `n` is greater than `xs.length`.
`O(min n |xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
Examples:
* `[a, b, c, d, e].takeTR 0 = []`
* `[a, b, c, d, e].takeTR 3 = [a, b, c]`
* `[a, b, c, d, e].takeTR 6 = [a, b, c, d, e]`
-/
@[inline] def takeTR (n : Nat) (l : List α) : List α := go l n #[] where
/-- Auxiliary for `take`: `take.go l xs n acc = acc.toList ++ take n xs`,
unless `n ≥ xs.length` in which case it returns `l`. -/
@ -146,7 +206,17 @@ The following operations are given `@[csimp]` replacements below:
/-! ### takeWhile -/
/-- Tail recursive version of `List.takeWhile`. -/
/--
Returns the longest initial segment of `xs` for which `p` returns true.
`O(|xs|)`. This is a tail-recursive version of `List.take`, used at runtime.
Examples:
* `[7, 6, 4, 8].takeWhileTR (· > 5) = [7, 6]`
* `[7, 6, 6, 5].takeWhileTR (· > 5) = [7, 6, 6]`
* `[7, 6, 6, 8].takeWhileTR (· > 5) = [7, 6, 6, 8]`
-/
@[inline] def takeWhileTR (p : α → Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `takeWhile`: `takeWhile.go p l xs acc = acc.toList ++ takeWhile p xs`,
unless no element satisfying `p` is found in `xs` in which case it returns `l`. -/
@ -169,7 +239,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### dropLast -/
/-- Tail recursive version of `dropLast`. -/
/--
Removes the last element of the list, if one exists.
This is a tail-recursive version of `List.dropLast`, used at runtime.
Examples:
* `[].dropLastTR = []`
* `["tea"].dropLastTR = []`
* `["tea", "coffee", "juice"].dropLastTR = ["tea", "coffee"]`
-/
@[inline] def dropLastTR (l : List α) : List α := l.toArray.pop.toList
@[csimp] theorem dropLast_eq_dropLastTR : @dropLast = @dropLastTR := by
@ -179,7 +258,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### replace -/
/-- Tail recursive version of `List.replace`. -/
/--
Replaces the first element of the list `l` that is equal to `a` with `b`. If no element is equal to
`a`, then the list is returned unchanged.
`O(|l|)`. This is a tail-recursive version of `List.replace` that's used in runtime code.
Examples:
* `[1, 4, 2, 3, 3, 7].replaceTR 3 6 = [1, 4, 2, 6, 3, 7]`
* `[1, 4, 2, 3, 3, 7].replaceTR 5 6 = [1, 4, 2, 3, 3, 7]`
-/
@[inline] def replaceTR [BEq α] (l : List α) (b c : α) : List α := go l #[] where
/-- Auxiliary for `replace`: `replace.go l b c xs acc = acc.toList ++ replace xs b c`,
unless `b` is not found in `xs` in which case it returns `l`. -/
@ -202,7 +290,16 @@ The following operations are given `@[csimp]` replacements below:
/-! ### modify -/
/-- Tail-recursive version of `modify`. -/
/--
Replaces the element at the given index, if it exists, with the result of applying `f` to it.
This is a tail-recursive version of `List.modify`.
Examples:
* `[1, 2, 3].modifyTR (· * 10) 0 = [10, 2, 3]`
* `[1, 2, 3].modifyTR (· * 10) 2 = [1, 2, 30]`
* `[1, 2, 3].modifyTR (· * 10) 3 = [1, 2, 3]`
-/
def modifyTR (f : αα) (n : Nat) (l : List α) : List α := go l n #[] where
/-- Auxiliary for `modifyTR`: `modifyTR.go f l n acc = acc.toList ++ modify f n l`. -/
go : List α → Nat → Array α → List α
@ -220,8 +317,22 @@ theorem modifyTR_go_eq : ∀ l i, modifyTR.go f l i acc = acc.toList ++ modify f
/-! ### insertIdx -/
/-- Tail-recursive version of `insertIdx`. -/
@[inline] def insertIdxTR (n : Nat) (a : α) (l : List α) : List α := go n l #[] where
/--
Inserts an element into a list at the specified index. If the index is greater than the length of
the list, then the list is returned unmodified.
In other words, the new element is inserted into the list `l` after the first `i` elements of `l`.
This is a tail-recursive version of `List.insertIdx`, used at runtime.
Examples:
* `["tues", "thur", "sat"].insertIdxTR 1 "wed" = ["tues", "wed", "thur", "sat"]`
* `["tues", "thur", "sat"].insertIdxTR 2 "wed" = ["tues", "thur", "wed", "sat"]`
* `["tues", "thur", "sat"].insertIdxTR 3 "wed" = ["tues", "thur", "sat", "wed"]`
* `["tues", "thur", "sat"].insertIdxTR 4 "wed" = ["tues", "thur", "sat"]`
-/
@[inline] def insertIdxTR (i : Nat) (a : α) (l : List α) : List α := go i l #[] where
/-- Auxiliary for `insertIdxTR`: `insertIdxTR.go a n l acc = acc.toList ++ insertIdx n a l`. -/
go : Nat → List α → Array α → List α
| 0, l, acc => acc.toListAppend (a :: l)
@ -237,7 +348,18 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
/-! ### erase -/
/-- Tail recursive version of `List.erase`. -/
/--
Removes the first occurrence of `a` from `l`. If `a` does not occur in `l`, the list is returned
unmodified.
`O(|l|)`.
This is a tail-recursive version of `List.erase`, used in runtime code.
Examples:
* `[1, 5, 3, 2, 5].eraseTR 5 = [1, 3, 2, 5]`
* `[1, 5, 3, 2, 5].eraseTR 6 = [1, 5, 3, 2, 5]`
-/
@[inline] def eraseTR [BEq α] (l : List α) (a : α) : List α := go l #[] where
/-- Auxiliary for `eraseTR`: `eraseTR.go l a xs acc = acc.toList ++ erase xs a`,
unless `a` is not present in which case it returns `l` -/
@ -257,7 +379,17 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
· rw [IH] <;> simp_all
· simp
/-- Tail-recursive version of `eraseP`. -/
/--
Removes the first element of a list for which `p` returns `true`. If no element satisfies `p`, then
the list is returned unchanged.
This is a tail-recursive version of `eraseP`, used at runtime.
Examples:
* `[2, 1, 2, 1, 3, 4].erasePTR (· < 2) = [2, 2, 1, 3, 4]`
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 2) = [2, 1, 2, 1, 4]`
* `[2, 1, 2, 1, 3, 4].erasePTR (· > 8) = [2, 1, 2, 1, 3, 4]`
-/
@[inline] def erasePTR (p : α → Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
@ -277,7 +409,20 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
/-! ### eraseIdx -/
/-- Tail recursive version of `List.eraseIdx`. -/
/--
Removes the element at the specified index. If the index is out of bounds, the list is returned
unmodified.
`O(i)`.
This is a tail-recursive version of `List.eraseIdx`, used at runtime.
Examples:
* `[0, 1, 2, 3, 4].eraseIdxTR 0 = [1, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdxTR 1 = [0, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdxTR 5 = [0, 1, 2, 3, 4]`
-/
@[inline] def eraseIdxTR (l : List α) (n : Nat) : List α := go l n #[] where
/-- Auxiliary for `eraseIdxTR`: `eraseIdxTR.go l n xs acc = acc.toList ++ eraseIdx xs a`,
unless `a` is not present in which case it returns `l` -/
@ -303,7 +448,18 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
/-! ### zipWith -/
/-- Tail recursive version of `List.zipWith`. -/
/--
Applies a function to the corresponding elements of two lists, stopping at the end of the shorter
list.
`O(min |xs| |ys|)`. This is a tail-recursive version of `List.zipWith` that's used at runtime.
Examples:
* `[1, 2].zipWithTR (· + ·) [5, 6] = [6, 8]`
* `[1, 2, 3].zipWithTR (· + ·) [5, 6, 10] = [6, 8, 13]`
* `[].zipWithTR (· + ·) [5, 6] = []`
* `[x₁, x₂, x₃].zipWithTR f [y₁, y₂, y₃, y₄] = [f x₁ y₁, f x₂ y₂, f x₃ y₃]`
-/
@[inline] def zipWithTR (f : α → β → γ) (as : List α) (bs : List β) : List γ := go as bs #[] where
/-- Auxiliary for `zipWith`: `zipWith.go f as bs acc = acc.toList ++ zipWith f as bs` -/
go : List α → List β → Array γ → List γ
@ -321,7 +477,16 @@ theorem insertIdxTR_go_eq : ∀ i l, insertIdxTR.go a i l acc = acc.toList ++ in
/-! ### zipIdx -/
/-- Tail recursive version of `List.zipIdx`. -/
/--
Pairs each element of a list with its index, optionally starting from an index other than `0`.
`O(|l|)`. This is a tail-recursive version of `List.zipIdx` that's used at runtime.
Examples:
* `[a, b, c].zipIdxTR = [(a, 0), (b, 1), (c, 2)]`
* `[a, b, c].zipIdxTR 5 = [(a, 5), (b, 6), (c, 7)]`
-/
def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) :=
let as := l.toArray
(as.foldr (fun a (n, acc) => (n-1, (a, n-1) :: acc)) (n + as.size, [])).2
@ -363,8 +528,18 @@ theorem enumFrom_eq_enumFromTR : @enumFrom = @enumFromTR := by
/-! ### intercalate -/
set_option linter.listVariables false in
/-- Tail recursive version of `List.intercalate`. -/
def intercalateTR (sep : List α) : List (List α) → List α
/--
Alternates the lists in `xs` with the separator `sep`.
This is a tail-recursive version of `List.intercalate` used at runtime.
Examples:
* `List.intercalateTR sep [] = []`
* `List.intercalateTR sep [a] = a`
* `List.intercalateTR sep [a, b] = a ++ sep ++ b`
* `List.intercalateTR sep [a, b, c] = a ++ sep ++ b ++ sep ++ c`
-/
def intercalateTR (sep : List α) : (xs : List (List α)) → List α
| [] => []
| [x] => x
| x::xs => go sep.toArray x xs #[]

View file

@ -2697,12 +2697,20 @@ theorem foldr_hom (f : β₁ → β₂) (g₁ : α → β₁ → β₁) (g₂ :
induction l <;> simp [*, H]
/--
Prove a proposition about the result of `List.foldl`,
by proving it for the initial data,
and the implication that the operation applied to any element of the list preserves the property.
A reasoning principle for proving propositions about the result of `List.foldl` by establishing an
invariant that is true for the initial data and preserved by the operation being folded.
The motive can take values in `Sort _`, so this may be used to construct data,
as well as to prove propositions.
Because the motive can return a type in any sort, this function may be used to construct data as
well as to prove propositions.
Example:
```lean example
example {xs : List Nat} : xs.foldl (· + ·) 1 > 0 := by
apply List.foldlRecOn
. show 0 < 1; trivial
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < b + a
intros; omega
```
-/
def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α → β) (b : β) (_ : motive b)
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op b a)), motive (List.foldl op b l)
@ -2723,12 +2731,20 @@ def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α
rfl
/--
Prove a proposition about the result of `List.foldr`,
by proving it for the initial data,
and the implication that the operation applied to any element of the list preserves the property.
A reasoning principle for proving propositions about the result of `List.foldr` by establishing an
invariant that is true for the initial data and preserved by the operation being folded.
The motive can take values in `Sort _`, so this may be used to construct data,
as well as to prove propositions.
Because the motive can return a type in any sort, this function may be used to construct data as
well as to prove propositions.
Example:
```lean example
example {xs : List Nat} : xs.foldr (· + ·) 1 > 0 := by
apply List.foldrRecOn
. show 0 < 1; trivial
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < a + b
intros; omega
```
-/
def foldrRecOn {motive : β → Sort _} : ∀ (l : List α) (op : α → β → β) (b : β) (_ : motive b)
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op a b)), motive (List.foldr op b l)

View file

@ -19,8 +19,11 @@ namespace List
/-! ## Operations using indexes -/
/--
Given a list `as = [a₀, a₁, ...]` and a function `f : (i : Nat) → α → (h : i < as.length) → β`, returns the list
`[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
Applies a function to each element of the list along with the index at which that element is found,
returning the list of results. In addition to the index, the function is also provided with a proof
that the index is valid.
`List.mapIdx` is a variant that does not provide the function with evidence that the index is valid.
-/
@[inline] def mapFinIdx (as : List α) (f : (i : Nat) → α → (h : i < as.length) → β) : List β :=
go as #[] (by simp)
@ -33,8 +36,11 @@ where
go as (acc.push (f acc.size a (by simp at h; omega))) (by simp at h ⊢; omega)
/--
Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁, ...]`, returns the list
`[f 0 a₀, f 1 a₁, ...]`.
Applies a function to each element of the list along with the index at which that element is found,
returning the list of results.
`List.mapFinIdx` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline] def mapIdx (f : Nat → α → β) (as : List α) : List β := go as #[] where
/-- Auxiliary for `mapIdx`:
@ -44,8 +50,12 @@ Given a function `f : Nat → α → β` and `as : List α`, `as = [a₀, a₁,
| a :: as, acc => go as (acc.push (f acc.size a))
/--
Given a list `as = [a₀, a₁, ...]` and a monadic function `f : (i : Nat) → α → (h : i < as.length) → m β`,
returns the list `[f 0 a₀ ⋯, f 1 a₁ ⋯, ...]`.
Applies a monadic function to each element of the list along with the index at which that element is
found, returning the list of results. In addition to the index, the function is also provided with a
proof that the index is valid.
`List.mapIdxM` is a variant that does not provide the function with evidence that the index is
valid.
-/
@[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) → α → (h : i < as.length) → m β) : m (List β) :=
go as #[] (by simp)
@ -58,8 +68,11 @@ where
go as (acc.push (← f acc.size a (by simp at h; omega))) (by simp at h ⊢; omega)
/--
Given a monadic function `f : Nat → α → m β` and `as : List α`, `as = [a₀, a₁, ...]`,
returns the list `[f 0 a₀, f 1 a₁, ...]`.
Applies a monadic function to each element of the list along with the index at which that element is
found, returning the list of results.
`List.mapFinIdxM` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline] def mapIdxM [Monad m] (f : Nat → α → m β) (as : List α) : m (List β) := go as #[] where
/-- Auxiliary for `mapIdxM`:

View file

@ -31,10 +31,13 @@ attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
/-! ### mapM -/
/-- Alternate (non-tail-recursive) form of mapM for proofs.
/--
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
results.
Note that we can not have this as the main definition and replace it using a `@[csimp]` lemma,
because they are only equal when `m` is a `LawfulMonad`.
This is a non-tail-recursive variant of `List.mapM` that's easier to reason about. It cannot be used
as the main definition and replaced by the tail-recursive version because they can only be proved
equal when `m` is a `LawfulMonad`.
-/
def mapM' [Monad m] (f : α → m β) : List α → m (List β)
| [] => pure []

View file

@ -17,10 +17,11 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/--
`ofFn f` with `f : fin n → α` returns the list whose ith element is `f i`
```
ofFn f = [f 0, f 1, ... , f (n - 1)]
```
Creates a list by applying `f` to each potential index in order, starting at `0`.
Examples:
* `List.ofFn (n := 3) toString = ["0", "1", "2"]`
* `List.ofFn (fun i => #["red", "green", "blue"].get i.val i.isLt) = ["red", "green", "blue"]`
-/
def ofFn {n} (f : Fin n → α) : List α := Fin.foldr n (f · :: ·) []

View file

@ -20,10 +20,14 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace List
/--
`O(min |l| |r|)`. Merge two lists using `le` as a switch.
Merges two lists, using `le` to select the first element of the resulting list if both are
non-empty.
This version is not tail-recursive,
but it is replaced at runtime by `mergeTR` using a `@[csimp]` lemma.
If both input lists are sorted according to `le`, then the resulting list is also sorted according
to `le`. `O(min |l| |r|)`.
This implementation is not tail-recursive, but it is replaced at runtime by a proven-equivalent
tail-recursive merge.
-/
def merge (xs ys : List α) (le : αα → Bool := by exact fun a b => a ≤ b) : List α :=
match xs, ys with
@ -54,16 +58,16 @@ def MergeSort.Internal.splitInTwo (l : { l : List α // l.length = n }) :
open MergeSort.Internal in
set_option linter.unusedVariables false in
/--
Simplified implementation of stable merge sort.
A stable merge sort.
This function is designed for reasoning about the algorithm, and is not efficient.
(It particular it uses the non tail-recursive `merge` function,
and so can not be run on large lists, but also makes unnecessary traversals of lists.)
It is replaced at runtime in the compiler by `mergeSortTR₂` using a `@[csimp]` lemma.
This function is a simplified implementation that's designed to be easy to reason about, rather than
for efficiency. In particular, it uses the non-tail-recursive `List.merge` function and traverses
lists unnecessarily.
Because we want the sort to be stable,
it is essential that we split the list in two contiguous sublists.
It is replaced at runtime by an efficient implementation that has been proven to be equivalent.
-/
-- Because we want the sort to be stable, it is essential that we split the list in two contiguous
-- sublists.
def mergeSort : ∀ (xs : List α) (le : αα → Bool := by exact fun a b => a ≤ b), List α
| [], _ => []
| [a], _ => [a]

View file

@ -18,7 +18,13 @@ def List.toArrayAux : List α → Array α → Array α
| nil, xs => xs
| cons a as, xs => toArrayAux as (xs.push a)
/-- Convert a `List α` into an `Array α`. This is O(n) in the length of the list. -/
/--
Converts a `List α` into an `Array α` by repeatedly pushing elements from the list onto an empty
array. `O(|xs|)`.
Use `List.toArray` instead of calling this function directly. At runtime, this operation implements
both `List.toArray` and `Array.mk`.
-/
-- This function is exported to C, where it is called by `Array.mk`
-- (the constructor) to implement this functionality.
@[inline, match_pattern, pp_nodot, export lean_list_to_array]

View file

@ -9,6 +9,14 @@ import Init.Data.Char.Basic
universe u
/--
Creates a string that contains the characters in a list, in order.
Examples:
* `['L', '∃', '∀', 'N'].asString = "L∃∀N"`
* `[].asString = ""`
* `['a', 'a', 'a'].asString = "aaa"`
-/
def List.asString (s : List Char) : String :=
⟨s⟩

View file

@ -40,6 +40,20 @@ instance {p : Prop} : ToString (Decidable p) := ⟨fun h =>
| Decidable.isTrue _ => "true"
| Decidable.isFalse _ => "false"⟩
/--
Converts a list into a string, using `ToString.toString` to convert its elements.
The resulting string resembles list literal syntax, with the elements separated by `", "` and
enclosed in square brackets.
The resulting string may not be valid Lean syntax, because there's no such expectation for
`ToString` instances.
Examples:
* `[1, 2, 3].toString = "[1, 2, 3]"`
* `["cat", "dog"].toString = "[cat, dog]"`
* `["cat", "dog", ""].toString = "[cat, dog, ]"`
-/
protected def List.toString [ToString α] : List α → String
| [] => "[]"
| [x] => "[" ++ toString x ++ "]"

View file

@ -2407,21 +2407,24 @@ Examples:
| none => none
/--
`List α` is the type of ordered lists with elements of type `α`.
It is implemented as a linked list.
Linked lists: ordered lists, in which each element has a reference to the next element.
Most operations on linked lists take time proportional to the length of the list, because each
element must be traversed to find the next element.
`List α` is isomorphic to `Array α`, but they are useful for different things:
* `List α` is easier for reasoning, and
`Array α` is modeled as a wrapper around `List α`
* `List α` works well as a persistent data structure, when many copies of the
tail are shared. When the value is not shared, `Array α` will have better
performance because it can do destructive updates.
* `List α` is easier for reasoning, and `Array α` is modeled as a wrapper around `List α`.
* `List α` works well as a persistent data structure, when many copies of the tail are shared. When
the value is not shared, `Array α` will have better performance because it can do destructive
updates.
-/
inductive List (α : Type u) where
/-- `[]` is the empty list. -/
/-- The empty list, usually written `[]`. -/
| nil : List α
/-- If `a : α` and `l : List α`, then `cons a l`, or `a :: l`, is the
list whose first element is `a` and with `l` as the rest of the list. -/
/--
The list whose first element is `head`, where `tail` is the rest of the list.
Usually written `head :: tail`.
-/
| cons (head : α) (tail : List α) : List α
instance {α} : Inhabited (List α) where
@ -2443,11 +2446,13 @@ protected def List.hasDecEq {α : Type u} [DecidableEq α] : (a b : List α) →
instance {α : Type u} [DecidableEq α] : DecidableEq (List α) := List.hasDecEq
/--
The length of a list: `[].length = 0` and `(a :: l).length = l.length + 1`.
The length of a list.
This function is overridden in the compiler to `lengthTR`, which uses constant
stack space, while leaving this function to use the "naive" recursion which is
easier for reasoning.
This function is overridden in the compiler to `lengthTR`, which uses constant stack space.
Examples:
* `([] : List String).length = 0`
* `["green", "brown"].length = 2`
-/
def List.length : List α → Nat
| nil => 0
@ -2459,40 +2464,69 @@ def List.lengthTRAux : List α → Nat → Nat
| cons _ as, n => lengthTRAux as (Nat.succ n)
/--
A tail-recursive version of `List.length`, used to implement `List.length`
without running out of stack space.
The length of a list.
This is a tail-recursive version of `List.length`, used to implement `List.length` without running
out of stack space.
Examples:
* `([] : List String).lengthTR = 0`
* `["green", "brown"].lengthTR = 2`
-/
def List.lengthTR (as : List α) : Nat :=
lengthTRAux as 0
/--
`as.get i` returns the `i`'th element of the list `as`.
This version of the function uses `i : Fin as.length` to ensure that it will
not index out of bounds.
Returns the element at the provided index, counting from `0`.
In other words, for `i : Fin as.length`, `as.get i` returns the `i`'th element of the list `as`.
Because the index is a `Fin` bounded by the list's length, the index will never be out of bounds.
Examples:
* `["spring", "summer", "fall", "winter"].get (2 : Fin 4) = "fall"`
* `["spring", "summer", "fall", "winter"].get (0 : Fin 4) = "spring"`
-/
def List.get {α : Type u} : (as : List α) → Fin as.length → α
| cons a _, ⟨0, _⟩ => a
| cons _ as, ⟨Nat.succ i, h⟩ => get as ⟨i, Nat.le_of_succ_le_succ h⟩
/--
`l.set n a` sets the value of list `l` at (zero-based) index `n` to `a`:
`[a, b, c, d].set 1 b' = [a, b', c, d]`
Replaces the value at (zero-based) index `n` in `l` with `a`. If the index is out of bounds, then
the list is returned unmodified.
Examples:
* `["water", "coffee", "soda", "juice"].set 1 "tea" = ["water", "tea", "soda", "juice"]`
* `["water", "coffee", "soda", "juice"].set 4 "tea" = ["water", "coffee", "soda", "juice"]`
-/
def List.set : List α → Nat → α → List α
def List.set : (l : List α) → (n : Nat) → (a : α) → List α
| cons _ as, 0, b => cons b as
| cons a as, Nat.succ n, b => cons a (set as n b)
| nil, _, _ => nil
/--
Folds a function over a list from the left:
`foldl f z [a, b, c] = f (f (f z a) b) c`
Folds a function over a list from the left, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in order, using `f`.
Examples:
* `[a, b, c].foldl f z = f (f (f z a) b) c`
* `[1, 2, 3].foldl (· ++ toString ·) "" = "123"`
* `[1, 2, 3].foldl (s!"({·} {·})") "" = "((( 1) 2) 3)"`
-/
@[specialize]
def List.foldl {α : Type u} {β : Type v} (f : α → β → α) : (init : α) → List β → α
| a, nil => a
| a, cons b l => foldl f (f a b) l
/-- `l.concat a` appends `a` at the *end* of `l`, that is, `l ++ [a]`. -/
/--
Adds an element to the *end* of a list.
The added element is the last element of the resulting list.
Examples:
* `List.concat ["red", "yellow"] "green" = ["red", "yellow", "green"]`
* `List.concat [1, 2, 3] 4 = [1, 2, 3, 4]`
* `List.concat [] () = [()]`
-/
def List.concat {α : Type u} : List αα → List α
| nil, b => cons b nil
| cons a as, b => cons a (concat as b)
@ -2718,10 +2752,14 @@ attribute [extern "lean_array_to_list"] Array.toList
attribute [extern "lean_array_mk"] Array.mk
/--
Converts a `List α` into an `Array α`. (This is preferred over the synonym `Array.mk`.)
Converts a `List α` into an `Array α`. `O(|xs|)`.
At runtime, this constructor is implemented by `List.toArrayImpl` and is O(n) in the length of the
list.
At runtime, this operation is implemented by `List.toArrayImpl` and takes time linear in the length
of the list. `List.toArray` should be used instead of `Array.mk`.
Examples:
* `[1, 2, 3].toArray = #[1, 2, 3]`
* `["monday", "wednesday", friday"].toArray = #["monday", "wednesday", friday"].`
-/
@[match_pattern]
abbrev List.toArray (xs : List α) : Array α := .mk xs

View file

@ -381,6 +381,9 @@ end Lean
open Lean (PersistentArray)
/--
Converts a list to a persistent array.
-/
def List.toPArray' {α : Type u} (xs : List α) : PersistentArray α :=
let rec loop : List α → PersistentArray α → PersistentArray α
| [], t => t

View file

@ -312,8 +312,16 @@ def Array.groupByKey [BEq α] [Hashable α] (key : β → α) (xs : Array β)
return groups
/--
Groups all elements `x`, `y` in `xs` with `key x == key y` into the same list
`(xs.groupByKey key).find! (key x)`. Groups preserve the relative order of elements in `xs`.
Groups the elements of a list `xs` according to the function `key`, returning a hash map in which
each group is associated with its key. Groups preserve the relative order of elements in `xs`.
Example:
```lean example
#eval [0, 1, 2, 3, 4, 5, 6].groupByKey (· % 2)
```
```output
Std.HashMap.ofList [(0, [0, 2, 4, 6]), (1, [1, 3, 5])]
```
-/
def List.groupByKey [BEq α] [Hashable α] (key : β → α) (xs : List β) :
Std.HashMap α (List β) :=

View file

@ -612,7 +612,7 @@
"end": {"line": 290, "character": 16}},
"contents":
{"value":
"```lean\nList.nil.{u} {α : Type u} : List α\n```\n***\n`[]` is the empty list. \n\nConventions for notations in identifiers:\n\n * The recommended spelling of `[]` in identifiers is `nil`.\n***\n*import Init.Prelude*",
"```lean\nList.nil.{u} {α : Type u} : List α\n```\n***\nThe empty list, usually written `[]`. \n\nConventions for notations in identifiers:\n\n * The recommended spelling of `[]` in identifiers is `nil`.\n***\n*import Init.Prelude*",
"kind": "markdown"}}
{"textDocument": {"uri": "file:///hover.lean"},
"position": {"line": 292, "character": 13}}
@ -621,7 +621,7 @@
"end": {"line": 292, "character": 15}},
"contents":
{"value":
"```lean\nList.cons.{u} {α : Type u} (head : α) (tail : List α) : List α\n```\n***\nIf `a : α` and `l : List α`, then `cons a l`, or `a :: l`, is the\nlist whose first element is `a` and with `l` as the rest of the list. \n\nConventions for notations in identifiers:\n\n * The recommended spelling of `::` in identifiers is `cons`.\n\n * The recommended spelling of `[a]` in identifiers is `singleton`.\n***\n*import Init.Prelude*",
"```lean\nList.cons.{u} {α : Type u} (head : α) (tail : List α) : List α\n```\n***\nThe list whose first element is `head`, where `tail` is the rest of the list.\nUsually written `head :: tail`.\n\n\nConventions for notations in identifiers:\n\n * The recommended spelling of `::` in identifiers is `cons`.\n\n * The recommended spelling of `[a]` in identifiers is `singleton`.\n***\n*import Init.Prelude*",
"kind": "markdown"}}
{"textDocument": {"uri": "file:///hover.lean"},
"position": {"line": 294, "character": 18}}
@ -630,7 +630,7 @@
"end": {"line": 294, "character": 20}},
"contents":
{"value":
"```lean\nList.map.{u, v} {α : Type u} {β : Type v} (f : α → β) : List α → List β\n```\n***\n`O(|l|)`. `map f l` applies `f` to each element of the list.\n* `map f [a, b, c] = [f a, f b, f c]`\n\n***\n*import Init.Data.List.Basic*",
"```lean\nList.map.{u, v} {α : Type u} {β : Type v} (f : α → β) (l : List α) : List β\n```\n***\nApplies a function to each element of the list, returning the resulting list of values.\n\n`O(|l|)`.\n\nExamples:\n* `[a, b, c].map f = [f a, f b, f c]`\n* `[].map Nat.succ = []`\n* `[\"one\", \"two\", \"three\"].map (·.length) = [3, 3, 5]`\n* `[\"one\", \"two\", \"three\"].map (·.reverse) = [\"eno\", \"owt\", \"eerht\"]`\n\n***\n*import Init.Data.List.Basic*",
"kind": "markdown"}}
{"textDocument": {"uri": "file:///hover.lean"},
"position": {"line": 297, "character": 26}}
@ -639,7 +639,7 @@
"end": {"line": 297, "character": 29}},
"contents":
{"value":
"```lean\nList.zip.{u, v} {α : Type u} {β : Type v} : List α → List β → List (α × β)\n```\n***\n`O(min |xs| |ys|)`. Combines the two lists into a list of pairs, with one element from each list.\nThe longer list is truncated to match the shorter list.\n* `zip [x₁, x₂, x₃] [y₁, y₂, y₃, y₄] = [(x₁, y₁), (x₂, y₂), (x₃, y₃)]`\n\n***\n*import Init.Data.List.Basic*",
"```lean\nList.zip.{u, v} {α : Type u} {β : Type v} : List α → List β → List (α × β)\n```\n***\nCombines two lists into a list of pairs in which the first and second components are the\ncorresponding elements of each list. The resulting list is the length of the shorter of the inputs\nlists.\n\n`O(min |xs| |ys|)`.\n\nExamples:\n* `[\"Mon\", \"Tue\", \"Wed\"].zip [1, 2, 3] = [(\"Mon\", 1), (\"Tue\", 2), (\"Wed\", 3)]`\n* `[\"Mon\", \"Tue\", \"Wed\"].zip [1, 2] = [(\"Mon\", 1), (\"Tue\", 2)]`\n* `[x₁, x₂, x₃].zip [y₁, y₂, y₃, y₄] = [(x₁, y₁), (x₂, y₂), (x₃, y₃)]`\n\n***\n*import Init.Data.List.Basic*",
"kind": "markdown"}}
{"textDocument": {"uri": "file:///hover.lean"},
"position": {"line": 297, "character": 19}}
@ -648,5 +648,5 @@
"end": {"line": 297, "character": 22}},
"contents":
{"value":
"```lean\nList.zip.{u, v} {α : Type u} {β : Type v} : List α → List β → List (α × β)\n```\n***\n`O(min |xs| |ys|)`. Combines the two lists into a list of pairs, with one element from each list.\nThe longer list is truncated to match the shorter list.\n* `zip [x₁, x₂, x₃] [y₁, y₂, y₃, y₄] = [(x₁, y₁), (x₂, y₂), (x₃, y₃)]`\n\n***\n*import Init.Data.List.Basic*",
"```lean\nList.zip.{u, v} {α : Type u} {β : Type v} : List α → List β → List (α × β)\n```\n***\nCombines two lists into a list of pairs in which the first and second components are the\ncorresponding elements of each list. The resulting list is the length of the shorter of the inputs\nlists.\n\n`O(min |xs| |ys|)`.\n\nExamples:\n* `[\"Mon\", \"Tue\", \"Wed\"].zip [1, 2, 3] = [(\"Mon\", 1), (\"Tue\", 2), (\"Wed\", 3)]`\n* `[\"Mon\", \"Tue\", \"Wed\"].zip [1, 2] = [(\"Mon\", 1), (\"Tue\", 2)]`\n* `[x₁, x₂, x₃].zip [y₁, y₂, y₃, y₄] = [(x₁, y₁), (x₂, y₂), (x₃, y₃)]`\n\n***\n*import Init.Data.List.Basic*",
"kind": "markdown"}}