This PR introduces slices of lists that are available via slice notation (e.g., `xs[1...5]`). * Moved the `take` combinator and the `List` iterator producer to `Init`. * Introduced a `toTake` combinator: `it.toTake` behaves like `it`, but it has the same type as `it.take n`. There is a constant cost per iteration compared to `it` itself. * Introduced `List` slices. Their iterators are defined as `suffixList.iter.take n` for upper-bounded slices and `suffixList.iter.toTake` for unbounded ones. Performance characteristics of using the slice `list[a...b]`: * when creating it: `O(a)` * every iterator step: `O(1)` * `toList`: `O(b - a + 1)` (given that a <= b) Because the slice only stores a suffix of `xs` internally, two slices can be equal even though the underlying lists differ in an irrelevant prefix. Because the `stop` field is allowed to be beyond the list's upper bound, the slices `[1][0...1]` and `[1][0...2]` are not equal, even though they effectively cover the same range of the same list. Improving this would require us to call `List.length` when building the slice, which would iterate through the whole list.
70 lines
1.6 KiB
Text
70 lines
1.6 KiB
Text
/-
|
||
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Paul Reichert
|
||
-/
|
||
module
|
||
|
||
prelude
|
||
public import Init.Data.Iterators.Combinators.Monadic.Take
|
||
|
||
@[expose] public section
|
||
|
||
namespace Std.Iterators
|
||
|
||
/--
|
||
Given an iterator `it` and a natural number `n`, `it.take n` is an iterator that outputs
|
||
up to the first `n` of `it`'s values in order and then terminates.
|
||
|
||
**Marble diagram:**
|
||
|
||
```text
|
||
it ---a----b---c--d-e--⊥
|
||
it.take 3 ---a----b---c⊥
|
||
|
||
it ---a--⊥
|
||
it.take 3 ---a--⊥
|
||
```
|
||
|
||
**Termination properties:**
|
||
|
||
* `Finite` instance: only if `it` is productive
|
||
* `Productive` instance: only if `it` is productive
|
||
|
||
**Performance:**
|
||
|
||
This combinator incurs an additional O(1) cost with each output of `it`.
|
||
-/
|
||
@[always_inline, inline]
|
||
def Iter.take {α : Type w} {β : Type w} [Iterator α Id β] (n : Nat) (it : Iter (α := α) β) :
|
||
Iter (α := Take α Id) β :=
|
||
it.toIterM.take n |>.toIter
|
||
|
||
/--
|
||
This combinator is only useful for advanced use cases.
|
||
|
||
Given a finite iterator `it`, returns an iterator that behaves exactly like `it` but is of the same
|
||
type as `it.take n`.
|
||
|
||
**Marble diagram:**
|
||
|
||
```text
|
||
it ---a----b---c--d-e--⊥
|
||
it.toTake ---a----b---c--d-e--⊥
|
||
```
|
||
|
||
**Termination properties:**
|
||
|
||
* `Finite` instance: always
|
||
* `Productive` instance: always
|
||
|
||
**Performance:**
|
||
|
||
This combinator incurs an additional O(1) cost with each output of `it`.
|
||
-/
|
||
@[always_inline, inline]
|
||
def Iter.toTake {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] (it : Iter (α := α) β) :
|
||
Iter (α := Take α Id) β :=
|
||
it.toIterM.toTake.toIter
|
||
|
||
end Std.Iterators
|