Library list_and_option
Inductive natlist : Set :=
| nil : natlist
| cons : nat -> natlist -> natlist.
Fixpoint append (l m : natlist) {struct l} : natlist :=
match l with
| nil => m
| cons x xs => cons x (append xs m)
end.
| nil : natlist
| cons : nat -> natlist -> natlist.
Fixpoint append (l m : natlist) {struct l} : natlist :=
match l with
| nil => m
| cons x xs => cons x (append xs m)
end.
We define how to map a function over a list.
This is Exercise 2 in the file list_ex.v
Fixpoint map (f : nat -> nat)(l: natlist) {struct l} : natlist :=
match l with
| nil => nil
| cons x xs => cons (f x) (map f xs)
end.
Lemma map_commutes_with_append : forall (f : nat->nat)(l k : natlist),
map f (append l k) = append (map f l)(map f k).
Proof.
intros.
induction l.
- simpl. auto.
- simpl. rewrite IHl. auto.
Qed.
We define how to fold a function over a list.
Exercise 1 in the file list_ex.v is an instance
of this where f = plus and n = 0
Fixpoint fold (f : nat -> nat -> nat)(n:nat)(l: natlist) {struct l} : nat :=
match l with
| nil => n
| cons x xs => f x (fold f n xs)
end.
We ask ourselves how fold and append commute
You may try to prove
Lemma fold_commutes_with_append : forall (f : nat->nat->nat)(n:nat)(l k : natlist), fold f n (append l k) = fold f (fold f n l) k.
and note that you can't prove that. And if you think about it, you note that it's not true.
This is the correct statement about how fold and append commute.
You may try to prove
Lemma fold_commutes_with_append : forall (f : nat->nat->nat)(n:nat)(l k : natlist), fold f n (append l k) = fold f (fold f n l) k.
and note that you can't prove that. And if you think about it, you note that it's not true.
This is the correct statement about how fold and append commute.
Lemma fold_commutes_with_append : forall (f : nat->nat->nat)(n:nat)(l k : natlist),
fold f n (append l k) = fold f (fold f n k) l.
Proof.
intros.
induction l.
- simpl. auto.
- simpl. rewrite IHl. simpl. auto.
Qed.
Check max.
Definition list_max := fold max 0.
Eval compute in (list_max (cons 4 (cons 8 (cons 0 (cons 7 nil))))).
Something that Rocq will not accept is the direct definition of merge,
because the recursive argument is not clear. A solution is to define two
function mutually by recursion.
The following is not accepted by Rocq
Fixpoint merge (l k :natlist) : natlist :=
match l, k with
| nil , nil => nil
| cons n l' , nil => cons n l'
| nil , cons m k' => cons m k'
| cons n l' , cons m k' =>
if Nat.eqb n m then cons n (merge l' k) else cons m (merge l k')
end.
Fixpoint merge (l k :natlist) : natlist :=
match l, k with
| nil , nil => nil
| cons n l' , nil => cons n l'
| nil , cons m k' => cons m k'
| cons n l' , cons m k' =>
if Nat.eqb n m then cons n (merge l' k) else cons m (merge l k')
end.
Fixpoint merge (l k :natlist){struct l} : natlist :=
let fix merge_aux k :=
match l, k with
| nil , nil => nil
| cons n l' , nil => cons n l'
| nil , cons m k' => cons m k'
| cons n l' , cons m k' =>
if Nat.leb n m then cons n (merge l' k) else cons m (merge_aux k')
end
in merge_aux k.
Eval compute in (merge (cons 4 (cons 8 nil)) (cons 0 (cons 7 nil))).
Note that tail of nil is nil -- we have to choose something
Note that head of nil is 0 -- we have to choose something
Lemma head_cons : forall x xs, head (cons x xs) = x.
Proof.
simpl.
auto.
Qed.
Lemma tail_cons : forall x xs, tail (cons x xs) = xs.
Proof.
simpl.
auto.
Qed.
Lemma cons_head_tail_firsttry : forall l, cons(head l)(tail l) = l.
Proof.
induction l.
- simpl.
This is just WRONG!
Abort.
An improved statement
Lemma cons_head_tail : forall l, ~ l = nil -> cons(head l)(tail l) = l.
Proof.
induction l.
- intro Hnil.
simpl.
unfold not in Hnil.
destruct Hnil.
auto.
Proof.
induction l.
- intro Hnil.
simpl.
unfold not in Hnil.
destruct Hnil.
auto.
Alternatively, congruence. solves your goal:
congruence does a sequence of rewriting and uses that
- distinct constructors are unequal
- t is not unequal to t
- intro H1.
simpl.
auto.
Qed.
simpl.
auto.
Qed.
The option type to deal with undefinedness
Print option.
Inductive option (A:Type) : Type :=
| Some : A -> option A
| None : option A.
Idea:
Now we define the basic operations of head and tail again
- if a: A, then Some a : option A, meaning that "a is a proper value of type A"
- None : option A, the "undefined element" of A
- otail : natlist -> option natlist
- ohead : natlist -> option nat
Definition otail (l : natlist) : option natlist :=
match l with
| nil => None
| cons x xs => Some xs
end.
match l with
| nil => None
| cons x xs => Some xs
end.
Now tail of nil is None
Fill in the correct function body in place of None
Check that head of nil is None
We also define ocons
Definition ocons (n : option nat)(l : option natlist) : option natlist :=
match n with
| None => None
| Some p =>
match l with
None => None
| Some k => Some (cons p k)
end
end.
match n with
| None => None
| Some p =>
match l with
None => None
| Some k => Some (cons p k)
end
end.
This can be shortened as follow
Definition ocons' (n : option nat)(l : option natlist) : option natlist :=
match n, l with
| None, _ => None
| _, None => None
| Some p, Some k => Some (cons p k)
end.
match n, l with
| None, _ => None
| _, None => None
| Some p, Some k => Some (cons p k)
end.
Lemma o_cons_head_tail : forall l, ocons(ohead l)(otail l) = l.
This is not well-typed!
But we do have the following. Fill in the proofs.
Lemma o_cons_head_tail1 : forall l, ocons(ohead l)(otail l) = None -> l = nil.
Proof.
Admitted.
Lemma o_cons_head_tail2 : forall l k, ocons(ohead l)(otail l) = Some k -> l = k.
Proof.
Admitted.
Now redefine
Functions on types generalize to functions on option-types:
if f : A -> B, then opt_fun f : option A -> option B
similarly for f : A -> B -> C
- ootail : option natlist -> option natlist
- oohead : option natlist -> option nat
Definition otp_fun (A B : Set)(f: A -> B) :=
fun x: option A =>
match x with
None => None
| Some a => Some (f a)
end.
Definition otp_fun2 (A B C: Set)(f: A -> B -> C) :=
fun (x : option A)(y:option B) =>
match x, y with
None , _ => None
| _ , None => None
| Some a, Some b => Some (f a b)
end.
Sometimes you know that one of the inputs is total.
Say we know that the first argument is really an a : A.
Given f : A -> B -> C, define opt_fun2t f : A -> option B -> option C