bookshelf/theorem-proving-in-lean/TheoremProvingInLean/Exercises7.lean

207 lines
6.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/-
# References
1. Avigad, Jeremy. Theorem Proving in Lean, n.d.
-/
-- Exercise 1
--
-- Try defining other operations on the natural numbers, such as multiplication,
-- the predecessor function (with `pred 0 = 0`), truncated subtraction (with
-- `n - m = 0` when `m` is greater than or equal to `n`), and exponentiation.
-- Then try proving some of their basic properties, building on the theorems we
-- have already proved.
--
-- Since many of these are already defined in Leans core library, you should
-- work within a namespace named hide, or something like that, in order to avoid
-- name clashes.
namespace ex1
-- As defined in the book.
inductive Nat where
| zero : Nat
| succ : Nat → Nat
namespace Nat
def add (m n : Nat) : Nat :=
match n with
| Nat.zero => m
| Nat.succ n => Nat.succ (add m n)
instance : Add Nat where
add := add
theorem add_zero (m : Nat) : m + Nat.zero = m :=
rfl
theorem add_succ (m n : Nat) : m + n.succ = (m + n).succ :=
rfl
theorem zero_add (n : Nat) : Nat.zero + n = n :=
Nat.recOn (motive := fun x => Nat.zero + x = x)
n
(show Nat.zero + Nat.zero = Nat.zero from rfl)
(fun (n : Nat) (ih : Nat.zero + n = n) =>
show Nat.zero + n.succ = n.succ from
calc
Nat.zero + n.succ
= (Nat.zero + n).succ := add_succ Nat.zero n
_ = n.succ := by rw [ih])
-- Additional definitions.
def mul (m n : Nat) : Nat :=
match n with
| Nat.zero => Nat.zero
| Nat.succ n => m + mul m n
def pred (n : Nat) : Nat :=
match n with
| Nat.zero => Nat.zero
| Nat.succ n => n
def sub (m n : Nat) : Nat :=
match n with
| Nat.zero => m
| Nat.succ n => sub (pred m) n
def exp (m n : Nat) : Nat :=
match n with
| Nat.zero => Nat.zero.succ
| Nat.succ n => mul m (exp m n)
end Nat
end ex1
-- Exercise 2
--
-- Define some operations on lists, like a `length` function or the `reverse`
-- function. Prove some properties, such as the following:
--
-- a. `length (s ++ t) = length s + length t`
-- b. `length (reverse t) = length t`
-- c. `reverse (reverse t) = t`
namespace ex2
variable {α : Type _}
theorem length_sum (s t : List α)
: List.length (s ++ t) = List.length s + List.length t :=
List.recOn s
(by rw [List.nil_append, List.length, Nat.zero_add])
(fun hd tl ih => by rw [
List.length,
List.cons_append,
List.length,
ih,
Nat.add_assoc,
Nat.add_comm t.length,
Nat.add_assoc
])
theorem length_inject_anywhere (x : α) (as bs : List α)
: List.length (as ++ [x] ++ bs) = List.length as + List.length bs + 1 := by
induction as with
| nil => simp
| cons head tail ih => calc
List.length (head :: tail ++ [x] ++ bs)
= List.length (tail ++ [x] ++ bs) + 1 := rfl
_ = List.length tail + List.length bs + 1 + 1 := by rw [ih]
_ = List.length tail + (List.length bs + 1) + 1 := by rw [Nat.add_assoc (List.length tail)]
_ = List.length tail + (1 + List.length bs) + 1 := by rw [Nat.add_comm (List.length bs)]
_ = List.length tail + 1 + List.length bs + 1 := by rw [Nat.add_assoc (List.length tail) 1]
_ = List.length (head :: tail) + List.length bs + 1 := rfl
theorem list_reverse_aux_append (as bs : List α)
: List.reverseAux as bs = List.reverse as ++ bs := by
induction as generalizing bs with
| nil => rw [List.reverseAux, List.reverse, List.reverseAux, List.nil_append]
| cons head tail ih => calc
List.reverseAux (head :: tail) bs
= List.reverseAux tail (head :: bs) := rfl
_ = List.reverse tail ++ (head :: bs) := by rw [ih]
_ = List.reverse tail ++ ([head] ++ bs) := rfl
_ = List.reverse tail ++ [head] ++ bs := by rw [List.append_assoc]
_ = List.reverseAux tail [head] ++ bs := by rw [ih]
_ = List.reverseAux (head :: tail) [] ++ bs := rfl
theorem length_reverse (t : List α)
: List.length (List.reverse t) = List.length t := by
induction t with
| nil => simp
| cons head tail ih => calc
List.length (List.reverse (head :: tail))
= List.length (List.reverseAux tail [head]) := rfl
_ = List.length (List.reverse tail ++ [head]) := by rw [list_reverse_aux_append]
_ = List.length (List.reverse tail) + List.length [head] := by simp
_ = List.length tail + List.length [head] := by rw [ih]
_ = List.length tail + 1 := rfl
_ = List.length [] + List.length tail + 1 := by simp
_ = List.length ([] ++ [head] ++ tail) := by rw [length_inject_anywhere]
_ = List.length (head :: tail) := rfl
theorem reverse_reverse_aux (as bs : List α)
: List.reverse (as ++ bs) = List.reverse bs ++ List.reverse as := by
induction as generalizing bs with
| nil => simp
| cons head tail ih => calc
List.reverse (head :: tail ++ bs)
= List.reverseAux (head :: tail ++ bs) [] := rfl
_ = List.reverseAux (tail ++ bs) [head] := rfl
_ = List.reverse (tail ++ bs) ++ [head] := by rw [list_reverse_aux_append]
_ = List.reverse bs ++ List.reverse tail ++ [head] := by rw [ih]
_ = List.reverse bs ++ (List.reverse tail ++ [head]) := by rw [List.append_assoc]
_ = List.reverse bs ++ (List.reverseAux tail [head]) := by rw [list_reverse_aux_append]
_ = List.reverse bs ++ (List.reverseAux (head :: tail) []) := rfl
_ = List.reverse bs ++ List.reverse (head :: tail) := rfl
theorem reverse_reverse (t : List α)
: List.reverse (List.reverse t) = t := by
induction t with
| nil => simp
| cons head tail ih => calc
List.reverse (List.reverse (head :: tail))
= List.reverse (List.reverseAux tail [head]) := rfl
_ = List.reverse (List.reverse tail ++ [head]) := by rw [list_reverse_aux_append]
_ = List.reverse [head] ++ List.reverse (List.reverse tail) := by rw [reverse_reverse_aux]
_ = List.reverse [head] ++ tail := by rw [ih]
_ = [head] ++ tail := by simp
_ = head :: tail := rfl
end ex2
-- Exercise 3
--
-- Define an inductive data type consisting of terms built up from the following
-- constructors:
--
-- • `const n`, a constant denoting the natural number `n`
-- • `var n`, a variable, numbered `n`
-- • `plus s t`, denoting the sum of `s` and `t`
-- • `times s t`, denoting the product of `s` and `t`
--
-- Recursively define a function that evaluates any such term with respect to an
-- assignment of values to the variables.
namespace ex3
inductive Foo : Type _
| const : Nat → Foo
| var : Nat → Foo
| plus : Foo → Foo → Foo
| times : Foo → Foo → Foo
def value_at : Nat → List Nat → Nat
| _, [] => default
| 0, vs => List.head! vs
| (i + 1), vs => value_at i (List.tail! vs)
-- The provided "variables" are supplied in a 0-indexed list.
def eval_foo : Foo → List Nat → Nat
| (Foo.const n) , _ => n
| (Foo.var n) , vs => value_at n vs
| (Foo.plus m n) , vs => eval_foo m vs + eval_foo n vs
| (Foo.times m n), vs => eval_foo m vs * eval_foo n vs
end ex3