From 38e3401de20711596113c6ab0024efdd1fbe5018 Mon Sep 17 00:00:00 2001 From: matitaweb Date: Mon, 12 Dec 2011 14:32:47 +0000 Subject: [PATCH] commit by user andrea --- weblib/tutorial/chapter3.ma | 96 +++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/weblib/tutorial/chapter3.ma b/weblib/tutorial/chapter3.ma index 20e545e37..45f834a86 100644 --- a/weblib/tutorial/chapter3.ma +++ b/weblib/tutorial/chapter3.ma @@ -8,25 +8,26 @@ inductive list (A:Type[0]) : Type[0] ≝ | nil: list A | cons: A -> list A -> list A. -(* The type notation list A is the type of all lists with elements of type A: it is -defined by two constructors: a polymorphic empty list (nil A) and a cons operation, -adding a new head element of type A to a previous list. For instance, (list nat) and -and (list bool) are lists of natural numbers and booleans, respectively. But we can -also form more complex data types, like (list (list (nat → nat))), that is a list whose -elements are lists of functions from natural numbers to natural numbers. +(* The type notation list A is the type of all lists with elements of type A: +it is defined by two constructors: a polymorphic empty list (nil A) and a cons +operation, adding a new head element of type A to a previous list. For instance, +(list nat) and and (list bool) are lists of natural numbers and booleans, +respectively. But we can also form more complex data types, like +(list (list (nat → nat))), that is a list whose elements are lists of functions +from natural numbers to natural numbers. Typical elements in (list bool) are for instance, - nil nat - the empty list of type nat - cons nat true (nil nat) - the list containing true - cons nat false (cons nat (true (nil nat))) - the list containing false and true + nil nat - the empty list of type nat + cons nat true (nil nat) - the list containing true + cons nat false (cons nat (true (nil nat))) - the list containing false and true and so on. -Note that both constructos nil and cons are expecting in input the type parameter - +Note that both constructos nil and cons are expecting in input the type parameter: in this case, bool. -We now add a bit of notation, in order to make the syntax more readable. In particular, -we would like to write [] instead of (nil A) and a::l instead of (cons A a l), leaving -the system the burden to infer A, whenever possible. +We now add a bit of notation, in order to make the syntax more readable. In +particular, we would like to write [] instead of (nil A) and a::l instead of +(cons A a l), leaving the system the burden to infer A, whenever possible. *) notation "hvbox(hd break :: tl)" @@ -44,12 +45,12 @@ notation "hvbox(l1 break @ l2)" interpretation "nil" 'nil = (nil ?). interpretation "cons" 'cons hd tl = (cons ? hd tl). -(* Let us define a few basic functions over lists. Our first example is the append -function, concatenating two lists l1 and l2. The natural way is to proceed by recursion -on l1: if it is empty the result is simply l2, while if l1 = hd::tl then we -recursively append tl and l2 , and then add hd as first element. Note that the append -function itself is polymorphic, and the first argument it takes in input is the type -A of the elements of two lists l1 and l2. +(* Let us define a few basic functions over lists. Our first example is the +append function, concatenating two lists l1 and l2. The natural way is to proceed +by recursion on l1: if it is empty the result is simply l2, while if l1 = hd::tl +then we recursively append tl and l2 , and then add hd as first element. Note that +the append function itself is polymorphic, and the first argument it takes in input +is the type A of the elements of two lists l1 and l2. Moreover, since the append function takes in input several parameters, we must also specify in the its defintion on which one of them we are recurring: in this case l1. If not othewise specified, recursion is supposed to act on the first argument of the @@ -68,18 +69,18 @@ l, as shown by the following example: *) example nil_append: ∀A.∀l:a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A. a title="nil" href="cic:/fakeuri.def(1)"[/a] a title="append" href="cic:/fakeuri.def(1)"@/a l a title="leibnitz's equality" href="cic:/fakeuri.def(1)"=/a l. #A #l normalize // qed. -(* Proving that l @ [] = l is just a bit more complex. The situation is exactly the -same as for the addition operation of the previous chapter: since append is defined -by recutsion over the first argument, the computation of l @ [] is stuck, and we must -proceed by induction on l *) +(* Proving that l @ [] = l is just a bit more complex. The situation is exactly +the same as for the addition operation of the previous chapter: since append is +defined by recutsion over the first argument, the computation of l @ [] is stuck, +and we must proceed by induction on l *) lemma append_nil: ∀A.∀l:a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A.l a title="append" href="cic:/fakeuri.def(1)"@/a a title="nil" href="cic:/fakeuri.def(1)"[/a] a title="leibnitz's equality" href="cic:/fakeuri.def(1)"=/a l. #A #l (elim l) normalize // qed. (* similarly, we can define the two functions head and tail. Since we can only define -total functions, we should decide what to do in case the input list is empty. For tl, it -is natural to return the empty list; for hd, we take in input a default element d of type -A to return in this case. *) +total functions, we should decide what to do in case the input list is empty. +For tl, it is natural to return the empty list; for hd, we take in input a default +element d of type A to return in this case. *) definition head ≝ λA.λl: a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A.λd:A. match l with [ nil ⇒ d | cons a _ ⇒ a]. @@ -132,10 +133,10 @@ and l2 just requires a trivial induction on the first list. *) a href="cic:/matita/tutorial/chapter3/length.fix(0,1,1)"length/a ? (l1a title="append" href="cic:/fakeuri.def(1)"@/al2) a title="leibnitz's equality" href="cic:/fakeuri.def(1)"=/a a href="cic:/matita/tutorial/chapter2/add.fix(0,0,1)"add/a (a href="cic:/matita/tutorial/chapter3/length.fix(0,1,1)"length/a ? l1) (a href="cic:/matita/tutorial/chapter3/length.fix(0,1,1)"length/a ? l2). #A #l1 elim l1 normalize // qed. -(* Let us come to a more interesting question. How can we prove that the empty list is -different from any list with at least one element, that is from any list of the kind (a::l)? -We start defining a simple predicate stating if a list is empty or not. The predicate -is computed by inspection over the list *) +(* Let us come to a more interesting question. How can we prove that the empty +list is different from any list with at least one element, that is from any list +of the kind (a::l)? We start defining a simple predicate stating if a list is +empty or not. The predicate is computed by inspection over the list *) definition is_nil: ∀A:Type[0].a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A → Prop ≝ λA.λl.match l with [ nil ⇒ l a title="leibnitz's equality" href="cic:/fakeuri.def(1)"=/a a title="nil" href="cic:/fakeuri.def(1)"[/a] | cons hd tl ⇒ (l a title="leibnitz's non-equality" href="cic:/fakeuri.def(1)"≠/a a title="nil" href="cic:/fakeuri.def(1)"[/a])]. @@ -150,21 +151,22 @@ theorem diff_cons_nil: ∀A:Type[0].∀l:a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A.∀a:A. aa title="cons" href="cic:/fakeuri.def(1)":/a:l a title="leibnitz's non-equality" href="cic:/fakeuri.def(1)"≠/a a title="nil" href="cic:/fakeuri.def(1)"[/a]. #A #l #a @a href="cic:/matita/tutorial/chapter3/neg_aux.def(3)"neg_aux/a #Heq (* we start assuming the new hypothesis Heq of type a::l = [] using neg_aux. -Next we use the change tactic to pass from the current goal a::l≠ [] to the expression -is_nil a::l, convertible with it. *) +Next we use the change tactic to pass from the current goal a::l≠ [] to the +expression is_nil a::l, convertible with it. *) (change with (a href="cic:/matita/tutorial/chapter3/is_nil.def(1)"is_nil/a ? (aa title="cons" href="cic:/fakeuri.def(1)":/a:l))) (* Now, we rewrite with Heq, obtaining (is_nil A []), that reduces to the trivial goal [] = [] *) >Heq // qed. -(* As an application of the previous result let us prove that l1@l2 is empty if and -only if both l1 and l2 are empty. The idea is to proceed by cases on l1: if l1=[] the -statement is trivial; on the other side, if l1 = a::tl, then the hypothesis -(a::tl)@l2 = [] is absurd, hence we can prove anything from it. When we know we can -prove both A and ¬A, a sensible way to proceed is to apply False_ind: ∀P.False → P to the -current goal, that breaks down to prove False, and then absurd: ∀A:Prop. A → ¬A → False -to reduce to the contradictory cases. Usually, you may invoke automation to take care -to solve the absurd case. *) +(* As an application of the previous result let us prove that l1@l2 is empty if +and only if both l1 and l2 are empty. +The idea is to proceed by cases on l1: if l1=[] the statement is trivial; on the +other side, if l1 = a::tl, then the hypothesis (a::tl)@l2 = [] is absurd, hence we +can prove anything from it. +When we know we can prove both A and ¬A, a sensible way to proceed is to apply +False_ind: ∀P.False → P to the current goal, that breaks down to prove False, and +then absurd: ∀A:Prop. A → ¬A → False to reduce to the contradictory cases. +Usually, you may invoke automation to take care to solve the absurd case. *) lemma nil_to_nil: ∀A.∀l1,l2:a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a span style="text-decoration: underline;"/spanA. l1a title="append" href="cic:/fakeuri.def(1)"@/al2 a title="leibnitz's equality" href="cic:/fakeuri.def(1)"=/a a title="nil" href="cic:/fakeuri.def(1)"[/a] → l1 a title="leibnitz's equality" href="cic:/fakeuri.def(1)"=/a a title="nil" href="cic:/fakeuri.def(1)"[/a] a title="logical and" href="cic:/fakeuri.def(1)"∧/a l2 a title="leibnitz's equality" href="cic:/fakeuri.def(1)"=/a a title="nil" href="cic:/fakeuri.def(1)"[/a]. @@ -185,12 +187,12 @@ l = [a1; a2; ... ;an], a base value b:B, and a function f: A → B → B returns let rec foldr (A,B:Type[0]) (f:A → B → B) (b:B) (l:a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A) on l :B ≝ match l with [ nil ⇒ b | cons a l ⇒ f a (foldr A B f b l)]. -(* As an example of application of foldr, let us use it to define a filter function -that given a list l: list A and a boolean test p:A → bool returns the sublist of elements -satisfying the test. In this case, the result type B of foldr should be (list A), the base -value is [], and f: A → list A →list A is the function that taken x and l returns x::l, if -x satisfies the test, and l otherwise. We use an if_then_else function included from -bbol.ma to this purpose. *) +(* As an example of application of foldr, let us use it to define a filter +function that given a list l: list A and a boolean test p:A → bool returns the +sublist of elements satisfying the test. In this case, the result type B of +foldr should be (list A), the base value is [], and f: A → list A →list A is +the function that taken x and l returns x::l, if x satisfies the test, and l +otherwise. We use an if_then_else function included from bool.ma to this purpose. *) definition filter ≝ λT.λp:T → a href="cic:/matita/basics/bool/bool.ind(1,0,0)"bool/a. @@ -260,7 +262,7 @@ A really convenient tool is the following combination of fold and filter, that essentially allow you to iterate on every subset of a given enumerated (finite) type, represented as a list. *) - let rec fold (A,B:Type[0]) (op:B → B → B) (b:B) (p:A→a href="cic:/matita/basics/bool/bool.ind(1,0,0)" title="null"bool/a) (f:A→B) (l:a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A) on l :B ≝ + let rec fold (A,B:Type[0]) (op:B →B →B) (b:B) (p:A→a href="cic:/matita/basics/bool/bool.ind(1,0,0)" title="null"bool/a) (f:A→B) (l:a href="cic:/matita/tutorial/chapter3/list.ind(1,0,1)"list/a A) on l :B ≝ match l with [ nil ⇒ b | cons a l ⇒ a href="cic:/matita/basics/bool/if_then_else.def(1)"if_then_else/a ? (p a) (op (f a) (fold A B op b p f l)) -- 2.39.2