2023-05-04 22:37:54 +00:00
|
|
|
|
/-! # Exercises.Avigad.Chapter8
|
2023-02-21 01:04:51 +00:00
|
|
|
|
|
2023-04-02 14:57:58 +00:00
|
|
|
|
Induction and Recursion
|
2023-02-12 14:17:07 +00:00
|
|
|
|
-/
|
|
|
|
|
|
2023-05-04 22:37:54 +00:00
|
|
|
|
namespace Exercises.Avigad.Chapter8
|
|
|
|
|
|
|
|
|
|
/-! #### Exercise 1
|
|
|
|
|
|
|
|
|
|
Open a namespace `Hidden` to avoid naming conflicts, and use the equation
|
|
|
|
|
compiler to define addition, multiplication, and exponentiation on the natural
|
|
|
|
|
numbers. Then use the equation compiler to derive some of their basic
|
|
|
|
|
properties.
|
|
|
|
|
-/
|
2023-04-08 16:32:20 +00:00
|
|
|
|
|
2023-02-12 14:17:07 +00:00
|
|
|
|
namespace ex1
|
|
|
|
|
|
|
|
|
|
def add : Nat → Nat → Nat
|
|
|
|
|
| m, Nat.zero => m
|
|
|
|
|
| m, Nat.succ n => Nat.succ (add m n)
|
|
|
|
|
|
|
|
|
|
def mul : Nat → Nat → Nat
|
|
|
|
|
| _, Nat.zero => 0
|
|
|
|
|
| m, Nat.succ n => add m (mul m n)
|
|
|
|
|
|
|
|
|
|
def exp : Nat → Nat → Nat
|
|
|
|
|
| _, Nat.zero => 1
|
|
|
|
|
| m, Nat.succ n => mul m (exp m n)
|
|
|
|
|
|
|
|
|
|
end ex1
|
|
|
|
|
|
2023-05-04 22:37:54 +00:00
|
|
|
|
/-! #### Exercise 2
|
|
|
|
|
|
|
|
|
|
Similarly, use the equation compiler to define some basic operations on lists
|
|
|
|
|
(like the reverse function) and prove theorems about lists by induction (such as
|
|
|
|
|
the fact that `reverse (reverse xs) = xs` for any list `xs`).
|
|
|
|
|
-/
|
2023-04-08 16:32:20 +00:00
|
|
|
|
|
2023-02-12 14:17:07 +00:00
|
|
|
|
namespace ex2
|
|
|
|
|
|
|
|
|
|
variable {α : Type _}
|
|
|
|
|
|
|
|
|
|
def reverse : List α → List α
|
|
|
|
|
| [] => []
|
|
|
|
|
| (head :: tail) => reverse tail ++ [head]
|
|
|
|
|
|
|
|
|
|
-- Proof of `reverse (reverse xs) = xs` shown in previous exercise.
|
|
|
|
|
|
|
|
|
|
end ex2
|
|
|
|
|
|
2023-05-04 22:37:54 +00:00
|
|
|
|
/-! #### Exercise 3
|
|
|
|
|
|
|
|
|
|
Define your own function to carry out course-of-value recursion on the natural
|
|
|
|
|
numbers. Similarly, see if you can figure out how to define `WellFounded.fix` on
|
|
|
|
|
your own.
|
|
|
|
|
-/
|
2023-04-08 16:32:20 +00:00
|
|
|
|
|
2023-02-12 14:17:07 +00:00
|
|
|
|
namespace ex3
|
|
|
|
|
|
2023-04-27 19:20:53 +00:00
|
|
|
|
def below {motive : Nat → Sort u} (t : Nat) : Sort (max 1 u) :=
|
|
|
|
|
Nat.recOn t
|
|
|
|
|
(zero := PUnit)
|
|
|
|
|
(succ := fun n ih => PProd (PProd (motive n) ih) (PUnit : Sort (max 1 u)))
|
|
|
|
|
|
|
|
|
|
/--
|
|
|
|
|
A copied implementation of `Nat.brecOn` with the `motive` properly supplied.
|
|
|
|
|
Notice the `noncomputable` tag; the code generator does not support the `recOn`
|
|
|
|
|
recursor yet, for reasons I haven't fully understood yet. This thread touches on
|
|
|
|
|
the topic:
|
|
|
|
|
|
|
|
|
|
https://leanprover-community.github.io/archive/stream/270676-lean4/topic/code.20generator.20does.20not.20support.20recursor.html
|
|
|
|
|
-/
|
|
|
|
|
noncomputable def brecOn {motive : Nat → Sort u}
|
|
|
|
|
(t : Nat) (F : (t : Nat) → @below motive t → motive t) : motive t :=
|
|
|
|
|
(Nat.recOn t
|
|
|
|
|
(motive := fun n => PProd
|
|
|
|
|
(motive n)
|
|
|
|
|
(Nat.recOn n PUnit fun n ih => PProd (PProd (motive n) ih) PUnit))
|
|
|
|
|
(zero := { fst := F Nat.zero PUnit.unit, snd := PUnit.unit })
|
|
|
|
|
(succ := fun n ih => {
|
|
|
|
|
fst := F (Nat.succ n) { fst := ih, snd := PUnit.unit },
|
|
|
|
|
snd := { fst := ih, snd := PUnit.unit }
|
|
|
|
|
})).1
|
|
|
|
|
|
|
|
|
|
#check WellFounded.fix
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
|
|
|
|
end ex3
|
|
|
|
|
|
2023-05-04 22:37:54 +00:00
|
|
|
|
/-! #### Exercise 4
|
|
|
|
|
|
|
|
|
|
Following the examples in Section Dependent Pattern Matching, define a function
|
|
|
|
|
that will append two vectors. This is tricky; you will have to define an
|
|
|
|
|
auxiliary function.
|
|
|
|
|
-/
|
2023-04-08 16:32:20 +00:00
|
|
|
|
|
2023-02-12 14:17:07 +00:00
|
|
|
|
namespace ex4
|
|
|
|
|
|
|
|
|
|
inductive Vector (α : Type u) : Nat → Type u
|
|
|
|
|
| nil : Vector α 0
|
|
|
|
|
| cons : α → {n : Nat} → Vector α n → Vector α (n + 1)
|
|
|
|
|
|
|
|
|
|
namespace Vector
|
|
|
|
|
|
2023-04-27 19:20:53 +00:00
|
|
|
|
/--
|
|
|
|
|
As long we flip the indices in our type signature in the resulting summation,
|
|
|
|
|
there is no need for an auxiliary function.
|
|
|
|
|
-/
|
|
|
|
|
def append : Vector α m → Vector α n → Vector α (n + m)
|
|
|
|
|
| nil, bs => bs
|
|
|
|
|
| cons a as, bs => cons a (append as bs)
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
|
|
|
|
end Vector
|
|
|
|
|
|
|
|
|
|
end ex4
|
|
|
|
|
|
2023-05-04 22:37:54 +00:00
|
|
|
|
/-! #### Exercise 5
|
|
|
|
|
|
|
|
|
|
Consider the following type of arithmetic expressions.
|
|
|
|
|
-/
|
2023-04-08 16:32:20 +00:00
|
|
|
|
|
2023-02-12 14:17:07 +00:00
|
|
|
|
namespace ex5
|
|
|
|
|
|
|
|
|
|
inductive Expr where
|
|
|
|
|
| const : Nat → Expr
|
|
|
|
|
| var : Nat → Expr
|
|
|
|
|
| plus : Expr → Expr → Expr
|
|
|
|
|
| times : Expr → Expr → Expr
|
|
|
|
|
deriving Repr
|
|
|
|
|
|
|
|
|
|
open Expr
|
|
|
|
|
|
|
|
|
|
def sampleExpr : Expr :=
|
|
|
|
|
plus (times (var 0) (const 7)) (times (const 2) (var 1))
|
|
|
|
|
|
|
|
|
|
-- Here `sampleExpr` represents `(v₀ * 7) + (2 * v₁)`. Write a function that
|
|
|
|
|
-- evaluates such an expression, evaluating each `var n` to `v n`.
|
|
|
|
|
|
|
|
|
|
def eval (v : Nat → Nat) : Expr → Nat
|
2023-04-27 19:20:53 +00:00
|
|
|
|
| const n => n
|
2023-02-12 14:17:07 +00:00
|
|
|
|
| var n => v n
|
2023-04-27 19:20:53 +00:00
|
|
|
|
| plus e₁ e₂ => eval v e₁ + eval v e₂
|
|
|
|
|
| times e₁ e₂ => eval v e₁ * eval v e₂
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
|
|
|
|
def sampleVal : Nat → Nat
|
|
|
|
|
| 0 => 5
|
|
|
|
|
| 1 => 6
|
|
|
|
|
| _ => 0
|
|
|
|
|
|
|
|
|
|
-- Try it out. You should get 47 here.
|
2023-04-27 19:20:53 +00:00
|
|
|
|
#eval eval sampleVal sampleExpr
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
2023-05-04 22:37:54 +00:00
|
|
|
|
/-! ##### Constant Fusion
|
|
|
|
|
|
|
|
|
|
Implement "constant fusion," a procedure that simplifies subterms like `5 + 7
|
|
|
|
|
to `12`. Using the auxiliary function `simpConst`, define a function "fuse": to
|
|
|
|
|
simplify a plus or a times, first simplify the arguments recursively, and then
|
|
|
|
|
apply `simpConst` to try to simplify the result.
|
|
|
|
|
-/
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
|
|
|
|
def simpConst : Expr → Expr
|
|
|
|
|
| plus (const n₁) (const n₂) => const (n₁ + n₂)
|
|
|
|
|
| times (const n₁) (const n₂) => const (n₁ * n₂)
|
|
|
|
|
| e => e
|
|
|
|
|
|
2023-04-27 19:20:53 +00:00
|
|
|
|
def fuse : Expr → Expr
|
|
|
|
|
| plus e₁ e₂ => simpConst $ plus (fuse e₁) (fuse e₂)
|
|
|
|
|
| times e₁ e₂ => simpConst $ times (fuse e₁) (fuse e₂)
|
|
|
|
|
| e => e
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
|
|
|
|
theorem simpConst_eq (v : Nat → Nat)
|
2023-04-27 19:20:53 +00:00
|
|
|
|
: ∀ e : Expr, eval v (simpConst e) = eval v e := by
|
|
|
|
|
intro e
|
|
|
|
|
unfold simpConst
|
|
|
|
|
repeat { unfold eval }
|
|
|
|
|
match h : e with
|
|
|
|
|
| const n
|
|
|
|
|
| var n
|
|
|
|
|
| plus (const _) (const _)
|
|
|
|
|
| plus (var _) _
|
|
|
|
|
| plus (plus _ _) _
|
|
|
|
|
| plus (times _ _) _
|
|
|
|
|
| times (const _) (const _)
|
|
|
|
|
| times (var _) _
|
|
|
|
|
| times (plus _ _) _
|
|
|
|
|
| times (times _ _) _ => rfl
|
|
|
|
|
| plus _ (var _)
|
|
|
|
|
| plus _ (plus _ _)
|
|
|
|
|
| plus _ (times _ _)
|
|
|
|
|
| times _ (var _)
|
|
|
|
|
| times _ (plus _ _)
|
|
|
|
|
| times _ (times _ _) => simp only
|
|
|
|
|
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
|
|
|
|
theorem fuse_eq (v : Nat → Nat)
|
2023-04-27 19:20:53 +00:00
|
|
|
|
: ∀ e : Expr, eval v (fuse e) = eval v e := by
|
|
|
|
|
intro e
|
|
|
|
|
induction e with
|
|
|
|
|
| const n | var n => unfold fuse; rfl
|
|
|
|
|
| plus e₁ e₂ he₁ he₂ | times e₁ e₂ he₁ he₂ =>
|
|
|
|
|
unfold fuse
|
|
|
|
|
rw [simpConst_eq]
|
|
|
|
|
unfold eval
|
|
|
|
|
rw [he₁, he₂]
|
2023-02-12 14:17:07 +00:00
|
|
|
|
|
|
|
|
|
-- The last two theorems show that the definitions preserve the value.
|
|
|
|
|
|
|
|
|
|
end ex5
|
2023-05-04 22:37:54 +00:00
|
|
|
|
|
|
|
|
|
end Exercises.Avigad.Chapter8
|