Zulip Chat Archive

Stream: new members

Topic: Termination of last


Iocta (Feb 18 2025 at 01:25):

I thought this would work because I'm shrinking the argument. What do I need to do?

def last {T : Type} (xs : List T) (h : xs  []) : T :=
  match xs.tail with
  | [] => xs.head h
  | x'::xs' => last (x'::xs') (xs'.cons_ne_nil x')
/-

fail to show termination for
  last
with errors
failed to infer structural recursion:
Not considering parameter T of last:
  it is unchanged in the recursive calls
Not considering parameter h of last:
  its type is not an inductive
Cannot use parameter xs:
  failed to eliminate recursive application
    last (x' :: xs') ⋯


failed to prove termination, possible solutions:
  - Use `have`-expressions to prove the remaining goals
  - Use `termination_by` to specify a different well-founded relation
  - Use `decreasing_by` to specify your own tactic for discharging this kind of goal
T : Type
xs : List T
h : xs ≠ []
x' : T
xs' : List T
⊢ 1 + sizeOf xs' < sizeOf xs
 -/

Eric Wieser (Feb 18 2025 at 01:27):

Does match h : xs.tail with help?

Iocta (Feb 18 2025 at 02:53):

This doesn't work if that's what you mean

def last {T : Type} (xs : List T) (ne_nil : xs  []) : T :=
  match h : xs.tail with
  | [] => (xs.head ne_nil)
  | x'::xs' => last (x'::xs') (xs'.cons_ne_nil x')

Kyle Miller (Feb 18 2025 at 03:20):

Lean doesn't know that xs.last is smaller. Matching on the result of a function rather than matching on an argument tends to cause problems like this.

Any reason you wouldn't want to write it like this?

def last {T : Type} (xs : List T) (h : xs  []) : T :=
  match xs, h with
  | [x],            _ => x
  | _ :: x' :: xs', _ => last (x'::xs') (xs'.cons_ne_nil x')

Kyle Miller (Feb 18 2025 at 03:25):

For your original one, the error message suggests some solutions. It says if you add a have with the sizeOf goal, you can get prove termination.

Using Eric's suggestion is the key to being able to prove that goal.

def last {T : Type} (xs : List T) (ne_nil : xs  []) : T :=
  match h : xs.tail with
  | [] => (xs.head ne_nil)
  | x'::xs' =>
    have : 1 + sizeOf xs' < sizeOf xs := by
      cases xs <;> cases h <;> simp
    last (x'::xs') (xs'.cons_ne_nil x')

Iocta (Feb 18 2025 at 03:54):

def last {T : Type} (xs : List T) (h : xs  []) : T :=
  match xs with
  | [x] => x
  | _ :: x' :: xs' => last (x'::xs') (xs'.cons_ne_nil x')

works yeah


Last updated: May 02 2025 at 03:31 UTC