Skip to content

Commit c981ebc

Browse files
authored
feat: split and splitInclusive iterators are finite (#10820)
This PR shows that the iterators returned by `String.Slice.split` and `String.Slice.splitInclusive` are finite as long as the forward matcher iterator for the pattern is finite (which we already know for all of our patterns). At actually also completely redefines the iterators to avoid the inner loop in `Internal.nextMatch` which generates inefficient code. Instead, when encountering a mismach from the matcher, we `skip` the split iterator.
1 parent 71f1a6c commit c981ebc

File tree

5 files changed

+248
-139
lines changed

5 files changed

+248
-139
lines changed

src/Init/Data/Iterators/Basic.lean

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,16 @@ def IterStep.successor : IterStep α β → Option α
241241
| .skip it => some it
242242
| .done => none
243243

244+
@[simp]
245+
theorem IterStep.successor_yield {it : α} {out : β} :
246+
(IterStep.yield it out).successor = some it := rfl
247+
248+
@[simp]
249+
theorem IterStep.successor_skip {it : α} : (IterStep.skip (β := β) it).successor = some it := rfl
250+
251+
@[simp]
252+
theorem IterStep.successor_done : (IterStep.done (α := α) (β := β)).successor = none := rfl
253+
244254
/--
245255
If present, applies `f` to the iterator of an `IterStep` and replaces the iterator
246256
with the result of the application of `f`.
@@ -543,6 +553,10 @@ def Iter.IsPlausibleSuccessorOf {α : Type w} {β : Type w} [Iterator α Id β]
543553
(it' it : Iter (α := α) β) : Prop :=
544554
it'.toIterM.IsPlausibleSuccessorOf it.toIterM
545555

556+
theorem Iter.isPlausibleSuccessorOf_eq_invImage {α : Type w} {β : Type w} [Iterator α Id β] :
557+
IsPlausibleSuccessorOf (α := α) (β := β) =
558+
InvImage (IterM.IsPlausibleSuccessorOf (α := α) (β := β) (m := Id)) Iter.toIterM := rfl
559+
546560
theorem Iter.isPlausibleSuccessorOf_iff_exists {α : Type w} {β : Type w} [Iterator α Id β]
547561
{it' it : Iter (α := α) β} :
548562
it'.IsPlausibleSuccessorOf it ↔ ∃ step, step.successor = some it' ∧ it.IsPlausibleStep step := by
@@ -555,6 +569,16 @@ theorem Iter.isPlausibleSuccessorOf_iff_exists {α : Type w} {β : Type w} [Iter
555569
exact ⟨step.mapIterator Iter.toIterM,
556570
by cases step <;> simp_all [IterStep.successor, Iter.IsPlausibleStep]⟩
557571

572+
theorem Iter.IsPlausibleStep.isPlausibleSuccessor_of_yield {α : Type w} {β : Type w}
573+
[Iterator α Id β] {it' it : Iter (α := α) β} {out : β}
574+
(h : it.IsPlausibleStep (.yield it' out)) : it'.IsPlausibleSuccessorOf it := by
575+
simpa [isPlausibleSuccessorOf_iff_exists] using ⟨.yield it' out, by simp [h]⟩
576+
577+
theorem Iter.IsPlausibleStep.isPlausibleSuccessor_of_skip {α : Type w} {β : Type w}
578+
[Iterator α Id β] {it' it : Iter (α := α) β} (h : it.IsPlausibleStep (.skip it')) :
579+
it'.IsPlausibleSuccessorOf it := by
580+
simpa [isPlausibleSuccessorOf_iff_exists] using ⟨.skip it', by simp [h]⟩
581+
558582
/--
559583
Asserts that a certain iterator `it` could plausibly yield the value `out` after an arbitrary
560584
number of steps.
@@ -656,6 +680,10 @@ Given this typeclass, termination proofs for well-founded recursion over an iter
656680
class Finite (α : Type w) (m : Type w → Type w') {β : Type w} [Iterator α m β] : Prop where
657681
wf : WellFounded (IterM.IsPlausibleSuccessorOf (α := α) (m := m))
658682

683+
theorem Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
684+
WellFounded (Iter.IsPlausibleSuccessorOf (α := α)) := by
685+
simpa [Iter.isPlausibleSuccessorOf_eq_invImage] using InvImage.wf _ Finite.wf
686+
659687
/--
660688
This type is a wrapper around `IterM` so that it becomes a useful termination measure for
661689
recursion over finite iterators. See also `IterM.finitelyManySteps` and `Iter.finitelyManySteps`.
@@ -705,6 +733,12 @@ theorem IterM.TerminationMeasures.Finite.rel_of_skip
705733
Rel ⟨it'⟩ ⟨it⟩ := by
706734
exact .single ⟨_, rfl, h⟩
707735

736+
theorem IterM.TerminationMeasures.Finite.rel_trans
737+
{α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator α m β]
738+
{it it' it'' : TerminationMeasures.Finite α m} :
739+
it.Rel it' → it'.Rel it'' → it.Rel it'' :=
740+
.trans
741+
708742
macro_rules | `(tactic| decreasing_trivial) => `(tactic|
709743
first
710744
| exact IterM.TerminationMeasures.Finite.rel_of_yield ‹_›

src/Init/Data/Iterators/Combinators/Monadic/FilterMap.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ it.filterMapWithPostcondition ---a'-----c'-------⊥
9494
**Termination properties:**
9595
9696
* `Finite` instance: only if `it` is finite
97-
* `Productive` instance: only if `it` is finite`
97+
* `Productive` instance: only if `it` is finite
9898
9999
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
100100
no `Finite` (or `Productive`) instance is provided. For example, if `f` never returns `none`, then

src/Init/Data/String/Pattern/Basic.lean

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ inductive SearchStep (s : Slice) where
3535
-/
3636
| rejected (startPos endPos : s.Pos)
3737
/--
38-
The subslice starting at {name}`startPos` and ending at {name}`endPos` did not match the pattern.
38+
The subslice starting at {name}`startPos` and ending at {name}`endPos` matches the pattern.
3939
-/
4040
| matched (startPos endPos : s.Pos)
4141
deriving Inhabited
@@ -99,44 +99,6 @@ where
9999
simp [Pos.Raw.lt_iff] at h ⊢
100100
omega
101101

102-
variable {ρ : Type} {σ : Slice → Type}
103-
variable [∀ s, Std.Iterators.Iterator (σ s) Id (SearchStep s)]
104-
variable [∀ s, Std.Iterators.Finite (σ s) Id]
105-
106-
/--
107-
Tries to skip the {name}`searcher` until the next {name}`SearchStep.matched` and return it. If no
108-
match is found until the end returns {name}`none`.
109-
-/
110-
@[inline]
111-
def nextMatch (searcher : Std.Iter (α := σ s) (SearchStep s)) :
112-
Option (Std.Iter (α := σ s) (SearchStep s) × s.Pos × s.Pos) :=
113-
go searcher
114-
where
115-
go [∀ s, Std.Iterators.Finite (σ s) Id] (searcher : Std.Iter (α := σ s) (SearchStep s)) :
116-
Option (Std.Iter (α := σ s) (SearchStep s) × s.Pos × s.Pos) :=
117-
match searcher.step with
118-
| .yield it (.matched startPos endPos) _ => some (it, startPos, endPos)
119-
| .yield it (.rejected ..) _ | .skip it .. => go it
120-
| .done .. => none
121-
termination_by Std.Iterators.Iter.finitelyManySteps searcher
122-
123-
/--
124-
Tries to skip the {name}`searcher` until the next {name}`SearchStep.rejected` and return it. If no
125-
reject is found until the end returns {name}`none`.
126-
-/
127-
@[inline]
128-
def nextReject (searcher : Std.Iter (α := σ s) (SearchStep s)) :
129-
Option (Std.Iter (α := σ s) (SearchStep s) × s.Pos × s.Pos) :=
130-
go searcher
131-
where
132-
go [∀ s, Std.Iterators.Finite (σ s) Id] (searcher : Std.Iter (α := σ s) (SearchStep s)) :
133-
Option (Std.Iter (α := σ s) (SearchStep s) × s.Pos × s.Pos) :=
134-
match searcher.step with
135-
| .yield it (.rejected startPos endPos) _ => some (it, startPos, endPos)
136-
| .yield it (.matched ..) _ | .skip it .. => go it
137-
| .done .. => none
138-
termination_by Std.Iterators.Iter.finitelyManySteps searcher
139-
140102
end Internal
141103

142104
namespace ForwardPattern

0 commit comments

Comments
 (0)