Zulip Chat Archive
Stream: new members
Topic: Termination of simple recursive function
amzorc (Dec 12 2023 at 02:15):
Hey, I am currently playing around with Lean for some functional programming, which has been mostly great so far! ATM, I am struggling to prove termination of this very simple function (actual version has more interesting logic ofc):
def foo {α} (p : α → Bool) : List α → ℕ
| [] => 0
| _ :: xs => let (as, bs) := xs.span p; foo p bs
I tried termination_by foo p xs => xs.length
, but I am not able to prove that List.length bs < Nat.succ (List.length xs)
in decreasing_by
, mostly (I think) because I don't know how to tell Lean that bs = (xs.span p).2
. Am I missing sth obvious?
(Termination is automatic when using separate takeWhile
/dropWhile
instead of span
, but I would really like to know how to prove it for span
as above.)
Shreyas Srinivas (Dec 12 2023 at 02:43):
I am guessing this can be significantly shortened. But here you go:
import Mathlib.Tactic
lemma list_drop_length : ∀ l : List α, ∀ p : α → Bool,
(l.dropWhile p).length < Nat.succ l.length := by
simp [Nat.succ_eq_one_add]
intro l p
unfold List.dropWhile
induction l with
| nil => simp
| cons head tail ih =>
simp
cases (p head) with
| false => simp
| true =>
simp
unfold List.dropWhile
trans 1 + List.length tail
exact ih
rw [Nat.succ_eq_one_add]
simp
def foo {α} (p : α → Bool) (l : List α) : ℕ :=
match l with
| [] => 0
| x :: xs =>
let abs := xs.span p
foo p abs.2
termination_by foo p l => l.length
decreasing_by
simp_wf
cases xs with
| nil =>
simp [List.dropWhile]
| cons head tail =>
apply list_drop_length
Shreyas Srinivas (Dec 12 2023 at 02:53):
You are losing the hypothesis for span
by destructuring the tuple at that let
amzorc (Dec 12 2023 at 02:56):
Thanks! So the takeaway is that there is just no way to use destructuring let
here?
Also FTR, I proved your list_drop_length
like this:
lemma list_drop_length : ∀ l : List α, ∀ p : α → Bool,
(l.dropWhile p).length < Nat.succ l.length := by
intro l p
rw [Nat.lt_succ]
nth_rw 2 [← List.takeWhile_append_dropWhile p l]
rw [List.length_append]
linarith
Shreyas Srinivas (Dec 12 2023 at 02:58):
I would argue that destructuring should work better by introducing hypothesis. At the very least it could add the hypothesesas = (xs.span p).fst
and bs = (xs.span p).snd
.
Shreyas Srinivas (Dec 12 2023 at 02:59):
Yeah that proof is much neater than mine
amzorc (Dec 12 2023 at 03:15):
Shreyas Srinivas said:
I would argue that destructuring should work better by introducing hypothesis. At the very least it could add the hypotheses
as = (xs.span p).fst
andbs = (xs.span p).snd
.
That would be nice, maybe something like let (as, bs) := xs.span p with h
and then you get h : (as, bs) = xs.span p
? I guess the closest thing is to use match
, ie
match h : xs.span p with
| (as, bs) => foo p bs
Notification Bot (Dec 12 2023 at 03:16):
amzorc has marked this topic as resolved.
Shreyas Srinivas (Dec 12 2023 at 03:24):
Please don't mark topics resolved btw. It messes with zulip links.
amzorc (Dec 12 2023 at 03:26):
Woops, does it help if I unresolve it?
Shreyas Srinivas (Dec 12 2023 at 03:27):
I guess. Not sure. I can't test it on my phone
Notification Bot (Dec 12 2023 at 03:34):
amzorc has marked this topic as unresolved.
Last updated: Dec 20 2023 at 11:08 UTC