|
| 1 | +namespace List |
| 2 | + |
| 3 | +/-- |
| 4 | + This theorem provides an induction principle for lists where the inductive step |
| 5 | + appends an element to the right (i.e., builds lists by snoc rather than cons). |
| 6 | + The intuition is that, instead of the usual head recursion, we want to prove a property |
| 7 | + for all lists by showing: |
| 8 | + - the property holds for the empty list, and |
| 9 | + - if it holds for a list `l`, then it holds for `l ++ [a]` for any `a`. |
| 10 | +-/ |
| 11 | +theorem induction_right {α : Type u} {P : List α → Prop} |
| 12 | + (h_nil : P []) |
| 13 | + (h_snoc : ∀ (l : List α) (a : α), P l → P (l ++ [a])) : |
| 14 | + ∀ l : List α, P l := by |
| 15 | + /- |
| 16 | + We want to prove P l for all lists l, using right induction. |
| 17 | + However, Lean's built-in induction on lists is left-sided (on cons), not right-sided (on snoc). |
| 18 | + To overcome this, we use a classic strengthening technique: |
| 19 | + instead of proving just P l, we prove a stronger property |
| 20 | + Q l := ∀ k, P k → P (k ++ l) |
| 21 | + This means: if we know P holds for any prefix k, then it also holds for k ++ l. |
| 22 | + Proving Q l for all l is strictly stronger than just P l, but it allows us to perform induction on l (using cons), |
| 23 | + and at the end, we recover the original goal by taking k = []. |
| 24 | + -/ |
| 25 | + intro l |
| 26 | + let Q := fun (l : List α) => ∀ (k : List α), P k → P (k ++ l) |
| 27 | + -- Base case: l = []. We must show Q []: ∀ k, P k → P (k ++ []). |
| 28 | + have h_base : Q [] := by |
| 29 | + intro k hk |
| 30 | + -- In this case, k ++ [] = k, so P (k ++ []) = P k, which is exactly hk. |
| 31 | + rw [List.append_nil] |
| 32 | + exact hk |
| 33 | + -- Inductive step: assume Q l', show Q (a :: l') for any a. |
| 34 | + have h_step : ∀ (a : α) (l' : List α), Q l' → Q (a :: l') := by |
| 35 | + intros a l' IH k hk |
| 36 | + /- |
| 37 | + Goal: P (k ++ (a :: l')) given P k. |
| 38 | + Observe: k ++ (a :: l') = (k ++ [a]) ++ l'. |
| 39 | + By the snoc hypothesis, from P k we get P (k ++ [a]). |
| 40 | + By the induction hypothesis (IH), from P (k ++ [a]) we get P ((k ++ [a]) ++ l'). |
| 41 | + Thus, we chain these two steps to get the result. |
| 42 | + -/ |
| 43 | + have hka : P (k ++ [a]) := h_snoc k a hk |
| 44 | + have : k ++ (a :: l') = (k ++ [a]) ++ l' := by simp [List.append_assoc] |
| 45 | + rw [this] |
| 46 | + exact IH (k ++ [a]) hka |
| 47 | + -- Now, by induction on l (using the usual left induction), we get Q l for all l. |
| 48 | + have hQ : ∀ l, Q l := by |
| 49 | + intro l' |
| 50 | + induction l' with |
| 51 | + | nil => exact h_base |
| 52 | + | cons a l'' IH => exact h_step a l'' IH |
| 53 | + -- Finally, to recover the original goal, specialize to k = [] and use h_nil : P []. |
| 54 | + exact hQ l [] h_nil |
| 55 | + |
| 56 | +end List |
0 commit comments