InfiniteList #
A note in the beginning: We mimic Mathlib's Stream' a lot here. Still I keep this separate to have full control about all the details. In the end, what we need should be simple enough.
This file defines an InfiniteList as a function from the naturals into the desired type.
We abstract away from this using many convenience functions.
For example, we offer head and tail to give the list a more coinductive flavor.
Also, there is mem_rec as a recursor over list elements to allow showing properties of list members via induction.
Furthermore, we offer a generate function that can build an infinite list from a function that builds a new element from a previous one.
An InfiniteList is defined as a function from the naturals into the desired type.
Equations
- InfiniteList α = (Nat → α)
Instances For
Basics #
The essential functions on infinite lists, mainly get, drop, head, and tail.
Obtains the nth element from the list.
Instances For
Obtain another infinite list by dropping the first n elements from the current one.
Instances For
Returns the first element.
Instances For
Drops the first element.
Instances For
Constructs an infinite list from an infinite list and a new head element.
Equations
- InfiniteList.cons hd tl Nat.zero = hd
- InfiniteList.cons hd tl n.succ = tl n
Instances For
Two infinite lists are the same of they are the same on all indices.
Each element we can get is in the list.
Get after drop can be rewritten into get.
Dropping zero elements changes nothing.
The head is in the list.
Getting the head after dropping n equals getting n.
Helper theorem stating the definition of tail.
Getting the nth element from the tail equals getting the successor of n from the original list.
Getting the tail after dropping n is the same as dropping n.succ.
Getting the first element on cons is the new head.
Getting any index > 0 on cons yields the respective element from the previous infinite list.
Any InfiniteList can be written using the cons constructor.
Suffixes #
Here, we define a suffix relation on InfiniteList inspired by List.IsSuffix.
For l1 and l2, l1 <:+ l2 denotes that l1 is a suffix of l2.
The suffix relation is reflexive and transitive but not necesarrily antisymmetric!
Equations
- InfiniteList.«term_<:+_» = Lean.ParserDescr.trailingNode `InfiniteList.«term_<:+_» 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " <:+ ") (Lean.ParserDescr.cat `term 51))
Instances For
The suffix relation is reflexive.
The suffix relation is transitive.
Two suffixes of the same list must be suffixes of each other in some way. This is similar to List.suffix_or_suffix_of_suffix.
A member of a suffix is also a member of the current list.
Dropping elements yields a suffix.
The tail is a suffix.
Recursor for Members #
We define a recursion (induction) principle for members (Elements) of an InfiniteList called mem_rec.
This can be used with the induction tactic to prove a property for each Element of an InfiniteList.
Note that for using this coveniently, the goal needs to expressed (rewritten) using an Element.
A recursor for proving properties about list members (Elements) via induction.
Map #
We allow to map over InfiniteList just like List.map.
Applies a function to each list element.
Instances For
When getting after map, we can instead get and then apply the mapping function.
The tail of a mapped list is the same as applying map to the tail.
Generating an InfiniteList #
We provide functions for "step-by-step" generation of an InfiniteList from a function building a next element from an existing one. This is very similar to what is done for Mathlib's Stream'.
Construct an infinite list by repeating a generator function. This is essentially Stream'.iterate from Mathlib.
Equations
- InfiniteList.iterate start generator Nat.zero = start
- InfiniteList.iterate start generator n.succ = generator (InfiniteList.iterate start generator n)
Instances For
When getting the successor of a number n from an interated list, we can instead get the nth element and apply the generator once mode.
When getting the successor of a number n from an interated list, we can instead apply the generator once initially, then iterate and then get the nth element.
When getting the sum of two numbers n+m from an interated list, we can instead generate the nth value, and use that as the starting value for another m iterations.
Instead of only iterating, we may want to create a kind of "carrier" list and then map this to the actually desired list. This is useful when the generator function requires more information that what actually ends up being in the desired list. Note that this is essentially the same as Stream'.corec from Mathlib.
Equations
- InfiniteList.generate start generator mapper = (InfiniteList.iterate start generator).map mapper
Instances For
The nth element of a generated list is the mapped version of the nth element of the iterated "carrier" list.
The successor of the nth element of a generated list can be seen as applying the mapper function after the generator function after taking the nth element from the iterated "carrier" list.
The successor of the nth element of a generated list can be seen as taking the nth element after initializing the generation process with the generator function already applied once in the beginning.
The tail of a generated list is the list generated when applying the generator function once on the starting element before the actual generation.
Take #
The take function puts the first n elements of an InfiniteList into a regular List.
The length of a taken list has exactly the desired number of elements.
When taking zero, you get nil.