How to do induction on the length of a list in Coq?
Question
When reasoning on paper, I often use arguments by induction on the length of some list. I want to formalized these arguments in Coq, but there doesn't seem to be any built in way to do induction on the length of a list.
How should I perform such an induction?
More concretely, I am trying to prove this theorem. On paper, I proved it by induction on the length of w. My goal is to formalize this proof in Coq.
Answer (Yves)
There are many general patterns of induction like this one that can be covered by the existing library on well founded induction. In this case, you can prove any property P by induction on length of lists by using well_founded_induction, wf_inverse_image, and PeanoNat.Nat.lt_wf_0, as in the following command:
T: Type
P: list T -> Prop
l: list T
H: forall y : list T, length y < length l -> P yP l
if you are working with lists of type T and proving a goal P l, this generates an hypothesis of the form
H : forall y : list T, length y < length l -> P y
This will apply to any other datatype (like trees for instance) as long as you can map that other datatype to nat using any size function from that datatype to nat instead of length.
Note that you need to add Require Import Wellfounded. at the head of your development for this to work.
A: Here is a slightly shorter variant:
T: Type
P: list T -> Prop
xs: list T
H: forall y : list T, length y < length xs -> P yP xs
But I'd prefer explicit naming:
T: Type
P: list T -> Prop
xs: list T
IHxs: forall y : list T, length y < length xs -> P yP xs
Answer (James Wilcox)
Here is how to prove a general list-length induction principle.
Require Import List Lia. Section list_length_ind. Variable A : Type. Variable P : list A -> Prop. Hypothesis H : forall xs, (forall l, length l < length xs -> P l) -> P xs.A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xsforall xs : list A, P xsA: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xsforall xs : list A, P xsA: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xsforall xs l : list A, length l <= length xs -> P lA: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
H_ind: forall xs l : list A, length l <= length xs -> P lforall xs : list A, P xsA: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xsforall xs l : list A, length l <= length xs -> P lA: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
l: list A
Hlen: length l <= length nil
l0: list A
H0: length l0 < length lP l0A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
a: A
xs: list A
IHxs: forall l : list A, length l <= length xs -> P l
l: list A
Hlen: length l <= length (a :: xs)
l0: list A
H0: length l0 < length lP l0A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
l: list A
Hlen: length l <= length nil
l0: list A
H0: length l0 < length lP l0lia.A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
l: list A
Hlen: length l <= length nil
l0: list A
H0: length l0 < length l
H2: length l = 0P l0A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
a: A
xs: list A
IHxs: forall l : list A, length l <= length xs -> P l
l: list A
Hlen: length l <= length (a :: xs)
l0: list A
H0: length l0 < length lP l0A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
a: A
xs: list A
IHxs: forall l : list A, length l <= length xs -> P l
l: list A
Hlen: length l <= length (a :: xs)
l0: list A
H0: length l0 < length llength l0 <= length xslia.A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
a: A
xs: list A
IHxs: forall l : list A, length l <= length xs -> P l
l: list A
Hlen: length l <= S (length xs)
l0: list A
H0: length l0 < length llength l0 <= length xsA: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
H_ind: forall xs l : list A, length l <= length xs -> P lforall xs : list A, P xsA: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
H_ind: forall xs l : list A, length l <= length xs -> P l
xs: list AP xslia. Qed. End list_length_ind.A: Type
P: list A -> Prop
H: forall xs : list A, (forall l : list A, length l < length xs -> P l) -> P xs
H_ind: forall xs l : list A, length l <= length xs -> P l
xs: list Alength xs <= length xs
You can use it like this
Theorem foo : forall l : list nat, ...
Proof.
induction l using list_length_ind.
...
That said, your concrete example example does not necessarily need induction on the length. You just need a sufficiently general induction hypothesis.
Require Import Coq.Init.Nat. Require Import Coq.Arith.PeanoNat. Require Import Coq.Lists.List. Import ListNotations. (* ... some definitions elided here ... *)
Definition flip_state (s : state) := match s with | A => B | B => A end. Definition delta (s : state) (n : input) : state := match n with | zero => s | one => flip_state s end. (* ...some more definitions elided here ...*)
forall w : list input, extend_delta A w = B <-> Nat.odd (one_num w) = trueforall w : list input, extend_delta A w = B <-> Nat.odd (one_num w) = trueforall (w : list input) (s : state), extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)H: forall (w : list input) (s : state), extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)forall w : list input, extend_delta A w = B <-> Nat.odd (one_num w) = trueforall (w : list input) (s : state), extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)s: states = si: input
w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: statedelta (extend_delta s w) i = (if Nat.odd match i with | zero => one_num w | one => S (one_num w) end then flip_state s else s)reflexivity.s: states = si: input
w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: statedelta (extend_delta s w) i = (if Nat.odd match i with | zero => one_num w | one => S (one_num w) end then flip_state s else s)i: input
w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: statedelta (if Nat.odd (one_num w) then flip_state s else s) i = (if Nat.odd match i with | zero => one_num w | one => S (one_num w) end then flip_state s else s)w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: state(if Nat.odd (one_num w) then flip_state s else s) = (if Nat.odd (one_num w) then flip_state s else s)w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: stateflip_state (if Nat.odd (one_num w) then flip_state s else s) = (if Nat.odd (S (one_num w)) then flip_state s else s)reflexivity.w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: state(if Nat.odd (one_num w) then flip_state s else s) = (if Nat.odd (one_num w) then flip_state s else s)w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: stateflip_state (if Nat.odd (one_num w) then flip_state s else s) = (if Nat.odd (S (one_num w)) then flip_state s else s)destruct (Nat.even (one_num w)), s; reflexivity.w: list input
IHw: forall s : state, extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
s: stateflip_state (if negb (Nat.even (one_num w)) then flip_state s else s) = (if Nat.even (one_num w) then flip_state s else s)H: forall (w : list input) (s : state), extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)forall w : list input, extend_delta A w = B <-> Nat.odd (one_num w) = trueH: forall (w : list input) (s : state), extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
w: list inputextend_delta A w = B <-> Nat.odd (one_num w) = trueH: forall (w : list input) (s : state), extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
w: list input(if Nat.odd (one_num w) then flip_state A else A) = B <-> Nat.odd (one_num w) = truedestruct (Nat.odd (one_num w)); intuition congruence. Qed.H: forall (w : list input) (s : state), extend_delta s w = (if Nat.odd (one_num w) then flip_state s else s)
w: list input(if Nat.odd (one_num w) then B else A) = B <-> Nat.odd (one_num w) = true
Answer (ejgallego)
In case like this, it is often faster to generalize your lemma directly:
Set Implicit Arguments. Unset Strict Implicit. Unset Printing Implicit Defensive. Section SO. Variable T : Type. Implicit Types (s : seq T) (P : seq T -> Prop).T: Type
P: seq T -> Prop
s: seq TP sT: Type
P: seq T -> Prop
s: seq TP sEnd SO.T: Type
P: seq T -> Prop
s: seq T
hs: size s <= 0P sT: Type
P: seq T -> Prop
ss: nat
ihss: forall s, size s <= ss -> P s
s: seq T
hs: size s <= ss.+1P s
Just introduce a fresh nat for the size of the list, and regular induction will work.