Acciones

Diferencia entre revisiones de «Tema 4»

De Seminario de Lógica Computacional (2018)

 
(No se muestran 3 ediciones intermedias de 2 usuarios)
Línea 1: Línea 1:
<source lang="ocaml">
+
<source lang="coq">
 
(* T4: Polimorfismo y funciones deo orden superior en Coq *)
 
(* T4: Polimorfismo y funciones deo orden superior en Coq *)
  
Línea 13: Línea 13:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo boollist para representar las listas de
+
   Ejemplo. Definir el tipo boollist para representar las listas de
 
   booleanos con los constructores bool_nil y bool_cons tales que  
 
   booleanos con los constructores bool_nil y bool_cons tales que  
 
   + bool_nil es la lista vacía y
 
   + bool_nil es la lista vacía y
Línea 26: Línea 26:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo (list X) para representar las listas de
+
   Ejemplo. Definir el tipo (list X) para representar las listas de
 
   elementos de con los constructores nil y cons tales que  
 
   elementos de con los constructores nil y cons tales que  
 
   + nil es la lista vacía y
 
   + nil es la lista vacía y
Línea 241: Línea 241:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo (list' {X}) para representar las listas de
+
   Ejemplo. Definir el tipo (list' {X}) para representar las listas de
 
   elementos de con los constructores nil y cons tales que  
 
   elementos de con los constructores nil y cons tales que  
 
   + nil' es la lista vacía y
 
   + nil' es la lista vacía y
Línea 308: Línea 308:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Especificar que la siguiente definición es errónea
+
   Ejemplo. Especificar que la siguiente definición es errónea
 
       Fail Definition mynil := nil.
 
       Fail Definition mynil := nil.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 316: Línea 316:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Completar la definición anterior para obtener la lista
+
   Ejemplo. Completar la definición anterior para obtener la lista
 
   vacía de números naturales.
 
   vacía de números naturales.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 326: Línea 326:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir las siguientes abreviaturas
+
   Ejemplo. Definir las siguientes abreviaturas
 
   + "x :: y"        para (cons x y)
 
   + "x :: y"        para (cons x y)
 
   + "[ ]"            para nil
 
   + "[ ]"            para nil
Línea 351: Línea 351:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que la lista vacía es el elemento neutro por la
+
   Ejercicio. Demostrar que la lista vacía es el elemento neutro por la
 
   derecha de la concatenación.
 
   derecha de la concatenación.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 358: Línea 358:
 
   l ++ [] = l.
 
   l ++ [] = l.
 
Proof.
 
Proof.
   (* FILL IN HERE *) Admitted.
+
   induction l.
 +
  + reflexivity.
 +
  + simpl. rewrite IHl. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que la concatenación es asociativa.
+
   Ejercicio. Demostrar que la concatenación es asociativa.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Línea 367: Línea 370:
 
   l ++ m ++ n = (l ++ m) ++ n.
 
   l ++ m ++ n = (l ++ m) ++ n.
 
Proof.
 
Proof.
   (* FILL IN HERE *) Admitted.
+
   intros A l m n.
 +
  induction l.
 +
  + reflexivity.
 +
  + simpl. rewrite IHl. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que la longitud de una concatenación es la suma de
+
   Ejercicio. Demostrar que la longitud de una concatenación es la suma de
 
   las longitudes de las listas (es decir, es un homomorfismo).
 
   las longitudes de las listas (es decir, es un homomorfismo).
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 377: Línea 384:
 
   length (l1 ++ l2) = length l1 + length l2.
 
   length (l1 ++ l2) = length l1 + length l2.
 
Proof.
 
Proof.
   (* FILL IN HERE *) Admitted.
+
   intros X l1 l2.
(** [] *)
+
  induction l1.
 +
  + reflexivity.
 +
  + simpl. rewrite IHl1. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       rev (l1 ++ l2) = rev l2 ++ rev l1.
 
       rev (l1 ++ l2) = rev l2 ++ rev l1.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 388: Línea 398:
 
   rev (l1 ++ l2) = rev l2 ++ rev l1.
 
   rev (l1 ++ l2) = rev l2 ++ rev l1.
 
Proof.
 
Proof.
   (* FILL IN HERE *) Admitted.
+
   intros X l1 l2.
 +
  induction l1.
 +
  + simpl. rewrite app_nil_r. reflexivity.
 +
  + simpl. rewrite IHl1, app_assoc. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       rev (rev l) = l.
 
       rev (rev l) = l.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 398: Línea 412:
 
   rev (rev l) = l.
 
   rev (rev l) = l.
 
Proof.
 
Proof.
   (* FILL IN HERE *) Admitted.
+
   induction l.
(** [] *)
+
  + reflexivity.
 +
  + simpl. rewrite rev_app_distr, IHl. reflexivity.
 +
Qed.
  
 
(* =====================================================================
 
(* =====================================================================
Línea 452: Línea 468:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y)  
 
       combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y)  
 
   tal que (combine lx ly) es la lista obtenida emparejando los
 
   tal que (combine lx ly) es la lista obtenida emparejando los
Línea 469: Línea 485:
 
       Check combine
 
       Check combine
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
 +
 +
Check @combine.
 +
(* ==> forall X Y : Type, list X -> list Y -> list (X * Y)*)
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 476: Línea 495:
  
 
Compute (combine [1;2] [false;false;true;true]).
 
Compute (combine [1;2] [false;false;true;true]).
 +
(* ==> [(1, false); (2, false)] : list (nat * bool) *)
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 485: Línea 505:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Fixpoint split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y)
+
Fixpoint split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y) :=
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
match l with
 +
| []          => ([], [])
 +
| (x, y) :: t => let s := split t in (x :: fst s, y :: snd s)
 +
end.
  
 
Example test_split:
 
Example test_split:
 
   split [(1,false);(2,false)] = ([1;2],[false;false]).
 
   split [(1,false);(2,false)] = ([1;2],[false;false]).
Proof.
+
Proof. reflexivity. Qed.
(* FILL IN HERE *) Admitted.
 
  
 
(* =====================================================================
 
(* =====================================================================
Línea 512: Línea 534:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       nth_error {X : Type} (l : list X) (n : nat) : option X :=
 
       nth_error {X : Type} (l : list X) (n : nat) : option X :=
 
   tal que (nth_error l n) es el n-ésimo elemento de l. Por ejemplo,  
 
   tal que (nth_error l n) es el n-ésimo elemento de l. Por ejemplo,  
Línea 541: Línea 563:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Definition hd_error {X : Type} (l : list X) : option X
+
Definition hd_error {X : Type} (l : list X) : option X :=
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
match l with
 +
    | []    => None
 +
    | x :: _ => Some x
 +
end.
  
 
Check @hd_error.
 
Check @hd_error.
  
 
Example test_hd_error1 : hd_error [1;2] = Some 1.
 
Example test_hd_error1 : hd_error [1;2] = Some 1.
  (* FILL IN HERE *) Admitted.
+
  Proof. reflexivity. Qed.
 
Example test_hd_error2 : hd_error  [[1];[2]]  = Some [1].
 
Example test_hd_error2 : hd_error  [[1];[2]]  = Some [1].
  (* FILL IN HERE *) Admitted.
+
  Proof. reflexivity. Qed.
  
 
(* =====================================================================
 
(* =====================================================================
Línea 612: Línea 637:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       countoddmembers' (l:list nat) : nat  
 
       countoddmembers' (l:list nat) : nat  
 
   tal que countoddmembers' l) es el número de elementos impares de
 
   tal que countoddmembers' l) es el número de elementos impares de
Línea 665: Línea 690:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Definition filter_even_gt7 (l : list nat) : list nat
+
Definition filter_even_gt7 (l : list nat) : list nat :=
   (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
   filter (fun x => evenb x && leb 7 x) l.
  
 
Example test_filter_even_gt7_1 :
 
Example test_filter_even_gt7_1 :
 
   filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
 
   filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
  (* FILL IN HERE *) Admitted.
+
  Proof. reflexivity. Qed.
  
 
Example test_filter_even_gt7_2 :
 
Example test_filter_even_gt7_2 :
 
   filter_even_gt7 [5;2;6;19;129] = [].
 
   filter_even_gt7 [5;2;6;19;129] = [].
(* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 690: Línea 715:
 
                     (test : X -> bool)
 
                     (test : X -> bool)
 
                     (l : list X)
 
                     (l : list X)
                   : list X * list X
+
                   : list X * list X :=
   (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
   (filter test l, filter (fun x => negb (test x)) l).
  
 
Example test_partition1: partition oddb [1;2;3;4;5] = ([1;3;5], [2;4]).
 
Example test_partition1: partition oddb [1;2;3;4;5] = ([1;3;5], [2;4]).
(* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
 
Example test_partition2: partition (fun x => false) [5;9;0] = ([], [5;9;0]).
 
Example test_partition2: partition (fun x => false) [5;9;0] = ([], [5;9;0]).
(* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
(* =====================================================================
 
(* =====================================================================
Línea 703: Línea 728:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       map {X Y:Type} (f:X->Y) (l:list X) : (list Y)  
 
       map {X Y:Type} (f:X->Y) (l:list X) : (list Y)  
 
   tal que (map f l) es la lista obtenida aplicando f a todos los
 
   tal que (map f l) es la lista obtenida aplicando f a todos los
Línea 732: Línea 757:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       map f (rev l) = rev (map f l).
 
       map f (rev l) = rev (map f l).
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
 +
 +
Lemma map_app_distr : forall (X Y : Type) (f : X -> Y) (l t : list X),
 +
    map f (l ++ t) = map f l ++ map f t.
 +
Proof.
 +
  intros X Y f l t.
 +
  induction l.
 +
  + reflexivity.
 +
  + simpl. rewrite IHl. reflexivity.
 +
Qed.
  
 
Theorem map_rev : forall (X Y : Type) (f : X -> Y) (l : list X),
 
Theorem map_rev : forall (X Y : Type) (f : X -> Y) (l : list X),
 
   map f (rev l) = rev (map f l).
 
   map f (rev l) = rev (map f l).
 
Proof.
 
Proof.
   (* FILL IN HERE *) Admitted.
+
   intros X Y f l.
 
+
  induction l.
 +
  + reflexivity.
 +
  + simpl. rewrite map_app_distr, IHl. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 750: Línea 787:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Fixpoint flat_map {X Y:Type} (f:X -> list Y) (l:list X)
+
Fixpoint flat_map {X Y:Type} (f:X -> list Y) (l:list X)  
                   : (list Y)
+
                   : (list Y) :=
   (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
  match l with
 +
   | [] => []
 +
  | x :: t => f x ++ flat_map f t
 +
  end.
  
 
Example test_flat_map1:
 
Example test_flat_map1:
 
   flat_map (fun n => [n;n;n]) [1;5;4]
 
   flat_map (fun n => [n;n;n]) [1;5;4]
 
   = [1; 1; 1; 5; 5; 5; 4; 4; 4].
 
   = [1; 1; 1; 5; 5; 5; 4; 4; 4].
(* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 886: Línea 926:
 
   fold_length l = length l.
 
   fold_length l = length l.
 
Proof.
 
Proof.
(* FILL IN HERE *) Admitted.
+
  intros X l.
 +
  unfold fold_length.
 +
  induction l.
 +
  - reflexivity.
 +
  - simpl. rewrite IHl. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 895: Línea 940:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Definition fold_map {X Y:Type} (f : X -> Y) (l : list X) : list Y
+
Definition fold_map {X Y:Type} (f : X -> Y) (l : list X) : list Y :=
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
  fold (fun x t => (f x) :: t) l [].
  
  
Línea 902: Línea 947:
 
   Ejemplo. Demostrar que fold_map es equivalente a map.
 
   Ejemplo. Demostrar que fold_map es equivalente a map.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
 +
 +
Theorem fold_map_correct : forall (X Y : Type) (f : X -> Y) (l : list X),
 +
    fold_map f l = map f l.
 +
Proof.
 +
  intros X Y f l.
 +
  unfold fold_map.
 +
  induction l.
 +
  - reflexivity.
 +
  - simpl. rewrite IHl. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 919: Línea 974:
  
 
Definition prod_uncurry {X Y Z : Type}
 
Definition prod_uncurry {X Y Z : Type}
   (f : X -> Y -> Z) (p : X * Y) : Z
+
   (f : X -> Y -> Z) (p : X * Y) : Z := f (fst p) (snd p).
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
 
  
 
Check @prod_curry.
 
Check @prod_curry.
Línea 926: Línea 980:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       prod_curry (prod_uncurry f) x y = f x y
 
       prod_curry (prod_uncurry f) x y = f x y
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 934: Línea 988:
 
                         x y,
 
                         x y,
 
   prod_curry (prod_uncurry f) x y = f x y.
 
   prod_curry (prod_uncurry f) x y = f x y.
Proof.
+
Proof. reflexivity. Qed.
  (* FILL IN HERE *) Admitted.
 
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       prod_uncurry (prod_curry f) p = f p.
 
       prod_uncurry (prod_curry f) p = f p.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 946: Línea 999:
 
   prod_uncurry (prod_curry f) p = f p.
 
   prod_uncurry (prod_curry f) p = f p.
 
Proof.
 
Proof.
   (* FILL IN HERE *) Admitted.
+
   intros.
 +
  destruct p.
 +
  reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       forall X n l, length l = n -> @nth_error X l n = None
 
       forall X n l, length l = n -> @nth_error X l n = None
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 1012: Línea 1068:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Definition succ (n : nat) : nat
+
Definition succ (n : nat) : nat :=
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
  fun (X : Type) (f : X -> X) (x : X) => f (n X f x).
  
 
Example succ_1 : succ zero = one.
 
Example succ_1 : succ zero = one.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example succ_2 : succ one = two.
 
Example succ_2 : succ one = two.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example succ_3 : succ two = three.
 
Example succ_3 : succ two = three.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 1033: Línea 1089:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Definition plus (n m : nat) : nat
+
Definition plus (n m : nat) : nat :=
   (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
   fun (X : Type) (f : X -> X) (x : X) => m X f (n X f x).
  
 
Example plus_1 : plus zero one = one.
 
Example plus_1 : plus zero one = one.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example plus_2 : plus two three = plus three two.
 
Example plus_2 : plus two three = plus three two.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example plus_3 :
 
Example plus_3 :
 
   plus (plus two two) three = plus one (plus three three).
 
   plus (plus two two) three = plus one (plus three three).
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 1055: Línea 1111:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Definition mult (n m : nat) : nat
+
Definition mult (n m : nat) : nat :=
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
  fun (X : Type) (f : X -> X) (x : X) => n X (m X f) x.
  
 
Example mult_1 : mult one one = one.
 
Example mult_1 : mult one one = one.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example mult_2 : mult zero (plus three three) = zero.
 
Example mult_2 : mult zero (plus three three) = zero.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example mult_3 : mult two three = plus three three.
 
Example mult_3 : mult two three = plus three three.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 1076: Línea 1132:
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Definition exp (n m : nat) : nat
+
Definition exp (n m : nat) : nat :=
   (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
+
   ( fun (X : Type) (f : X -> X) (x : X) => (m (X -> X) (n X) f) x).
  
 
Example exp_1 : exp two two = plus two two.
 
Example exp_1 : exp two two = plus two two.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example exp_2 : exp three two = plus (mult two (mult two two)) one.
 
Example exp_2 : exp three two = plus (mult two (mult two two)) one.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
Example exp_3 : exp three zero = one.
 
Example exp_3 : exp three zero = one.
Proof. (* FILL IN HERE *) Admitted.
+
Proof. reflexivity. Qed.
  
 
End Church.
 
End Church.

Revisión actual del 12:59 15 jul 2018

(* T4: Polimorfismo y funciones deo orden superior en Coq *)

Require Export T3_Listas.

(* =====================================================================
   § Polimorfismo
   ================================================================== *)

(* =====================================================================
   §§ Listas polimórficas  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo boollist para representar las listas de
   booleanos con los constructores bool_nil y bool_cons tales que 
   + bool_nil es la lista vacía y
   + (bool_cons x ys) es la lista obtenida añadiendo el booleano x a la
     lista ys.
   ------------------------------------------------------------------ *)

Inductive boollist : Type :=
  | bool_nil : boollist
  | bool_cons : bool -> boollist -> boollist.


(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo (list X) para representar las listas de
   elementos de con los constructores nil y cons tales que 
   + nil es la lista vacía y
   + (cons x ys) es la lista obtenida añadiendo el elemento x a la
     lista ys.
   ------------------------------------------------------------------ *)

Inductive list (X:Type) : Type :=
  | nil  : list X
  | cons : X -> list X -> list X.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de list.
   ------------------------------------------------------------------ *)

Check list.
(* ===> list : Type -> Type *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de (nil nat).
   ------------------------------------------------------------------ *)

Check (nil nat).
(* ===> nil nat : list nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de (cons nat 3 (nil nat)).
   ------------------------------------------------------------------ *)

Check (cons nat 3 (nil nat)).
(* ===> cons nat 3 (nil nat) : list nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de nil.
   ------------------------------------------------------------------ *)

Check nil.
(* ===> nil : forall X : Type, list X *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de cons.
   ------------------------------------------------------------------ *)

Check cons.
(* ===> cons : forall X : Type, X -> list X -> list X *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de (cons nat 2 (cons nat 1 (nil nat))).
   ------------------------------------------------------------------ *)

Check (cons nat 2 (cons nat 1 (nil nat))).
(* ==> cons nat 2 (cons nat 1 (nil nat)) : list nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat (X : Type) (x : X) (count : nat) : list X
   tal que (repeat X x n) es la lista obtenida repitiendo n veces el
   elemento x. Por ejemplo,
      repeat nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat (X : Type) (x : X) (count : nat) : list X :=
  match count with
  | 0 => nil X
  | S count' => cons X x (repeat X x count')
  end.

Example test_repeat1 :
  repeat nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
Proof. reflexivity.  Qed.

Example test_repeat2 :
  repeat bool false 1 = cons bool false (nil bool).
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Se definen los siguientes tipos
      Inductive mumble : Type :=
        | a : mumble
        | b : mumble -> nat -> mumble
        | c : mumble.
      
      Inductive grumble (X:Type) : Type :=
        | d : mumble -> grumble X
        | e : X -> grumble X.
  
   Decidir cuáles de los siguientes expresiones son del tipo (grumble X)
   para algún X:
      - [d (b a 5)]
      - [d mumble (b a 5)]
      - [d bool (b a 5)]
      - [e bool true]
      - [e mumble (b c 0)]
      - [e bool (b c 0)]
      - [c]
   ------------------------------------------------------------------ *)

Module MumbleGrumble.

Inductive mumble : Type :=
  | a : mumble
  | b : mumble -> nat -> mumble
  | c : mumble.

Inductive grumble (X:Type) : Type :=
  | d : mumble -> grumble X
  | e : X -> grumble X.

End MumbleGrumble.

(* =====================================================================
   §§§ Inferencia de tipos
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat' X x count : list X
   tal que (repeat' X x n) es la lista obtenida repitiendo n veces el
   elemento x. Por ejemplo,
      repeat' nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat' bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat' X x count : list X :=
  match count with
  | 0        => nil X
  | S count' => cons X x (repeat' X x count')
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular los tipos de repeat' y repeat.
   ------------------------------------------------------------------ *)

Check repeat'.
(* ===> forall X : Type, X -> nat -> list X *)
Check repeat.
(* ===> forall X : Type, X -> nat -> list X *)

(* =====================================================================
   §§§ Síntesis de los tipos de los argumentos  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat'' X x count : list X
   tal que (repeat'' X x n) es la lista obtenida repitiendo n veces el
   elemento x, usando argumentos implícitos. Por ejemplo,
      repeat'' nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat'' bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat'' X x count : list X :=
  match count with
  | 0        => nil _
  | S count' => cons _ x (repeat'' _ x count')
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la lista formada por los números naturales 1, 2 y 3.
   ------------------------------------------------------------------ *)

Definition list123 :=
  cons nat 1 (cons nat 2 (cons nat 3 (nil nat))).

Definition list123' :=
  cons _ 1 (cons _ 2 (cons _ 3 (nil _))).

(* =====================================================================
   §§§ Argumentos implícitos  
   ================================================================== *)


(* ---------------------------------------------------------------------
   Ejemplo. Especificar las siguientes funciones y sus argumentos
   explícitos e implícitos:
   + nil
   + constructor
   + repeat
   ------------------------------------------------------------------ *)

Arguments nil {X}.
Arguments cons {X} _ _.
Arguments repeat {X} x count.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la lista formada por los números naturales 1, 2 y 3.
   ------------------------------------------------------------------ *)

Definition list123'' := cons 1 (cons 2 (cons 3 nil)).

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat''' {X : Type} (x : X) (count : nat) : list X
   tal que (repeat'' X x n) es la lista obtenida repitiendo n veces el
   elemento x, usando argumentos implícitos. Por ejemplo,
      repeat'' nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat'' bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat''' {X : Type} (x : X) (count : nat) : list X :=
  match count with
  | 0        => nil
  | S count' => cons x (repeat''' x count')
  end.

Example test_repeat'''1 :
  repeat''' 4 2 = cons 4 (cons 4 nil).
Proof. reflexivity.  Qed.

Example test_repeat'''2 :
  repeat false 1 = cons false nil.
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo (list' {X}) para representar las listas de
   elementos de con los constructores nil y cons tales que 
   + nil' es la lista vacía y
   + (cons' x ys) es la lista obtenida añadiendo el elemento x a la
     lista ys.
   ------------------------------------------------------------------ *)

Inductive list' {X:Type} : Type :=
  | nil'  : list'
  | cons' : X -> list' -> list'.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      app {X : Type} (l1 l2 : list X) : (list X)
   tal que (app xs ys) es la concatenación de xs e ys.
   ------------------------------------------------------------------ *)

Fixpoint app {X : Type} (l1 l2 : list X) : (list X) :=
  match l1 with
  | nil      => l2
  | cons h t => cons h (app t l2)
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      rev {X:Type} (l:list X) : list X
   tal que (rev xs) es la inversa de xs. Por ejemplo,
      rev (cons 1 (cons 2 nil)) = (cons 2 (cons 1 nil)).
      rev (cons true nil) = cons true nil.
   ------------------------------------------------------------------ *)

Fixpoint rev {X:Type} (l:list X) : list X :=
  match l with
  | nil      => nil
  | cons h t => app (rev t) (cons h nil)
  end.

Example test_rev1 :
  rev (cons 1 (cons 2 nil)) = (cons 2 (cons 1 nil)).
Proof. reflexivity.  Qed.

Example test_rev2:
  rev (cons true nil) = cons true nil.
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      length {X : Type} (l : list X) : nat 
   tal que (length xs) es el número de elementos de xs. Por ejemplo,
      length (cons 1 (cons 2 (cons 3 nil))) = 3.
   ------------------------------------------------------------------ *)

Fixpoint length {X : Type} (l : list X) : nat :=
  match l with
  | nil       => 0
  | cons _ l' => S (length l')
  end.

Example test_length1:
  length (cons 1 (cons 2 (cons 3 nil))) = 3.
Proof. reflexivity.  Qed.

(* =====================================================================
   §§§ Explicitación de argumentos  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Especificar que la siguiente definición es errónea
      Fail Definition mynil := nil.
   ------------------------------------------------------------------ *)

Fail Definition mynil := nil.
(* ==> Error: Cannot infer the implicit parameter X of nil. *)

(* ---------------------------------------------------------------------
   Ejemplo. Completar la definición anterior para obtener la lista
   vacía de números naturales.
   ------------------------------------------------------------------ *)

Definition mynil : list nat := nil.

(* Alternativamente *)
Definition mynil' := @nil nat.

(* ---------------------------------------------------------------------
   Ejemplo. Definir las siguientes abreviaturas
   + "x :: y"         para (cons x y)
   + "[ ]"            para nil
   + "[ x ; .. ; y ]" para (cons x .. (cons y []) ..).
   + "x ++ y"         para (app x y)
   ------------------------------------------------------------------ *)

Notation "x :: y" := (cons x y)
                     (at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y []) ..).
Notation "x ++ y" := (app x y)
                     (at level 60, right associativity).

(* ---------------------------------------------------------------------
   Ejemplo. Definir la lista cuyos elementos son 1, 2 y 3.
   ------------------------------------------------------------------ *)

Definition list123''' := [1; 2; 3].

(* =====================================================================
   §§§ Ejercicios  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que la lista vacía es el elemento neutro por la
   derecha de la concatenación.
   ------------------------------------------------------------------ *)

Theorem app_nil_r : forall (X:Type), forall l:list X,
  l ++ [] = l.
Proof.
  induction l.
  + reflexivity.
  + simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que la concatenación es asociativa.
   ------------------------------------------------------------------ *)

Theorem app_assoc : forall A (l m n:list A),
  l ++ m ++ n = (l ++ m) ++ n.
Proof.
  intros A l m n.
  induction l.
  + reflexivity.
  + simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que la longitud de una concatenación es la suma de
   las longitudes de las listas (es decir, es un homomorfismo).
   ------------------------------------------------------------------ *)

Lemma app_length : forall (X:Type) (l1 l2 : list X),
  length (l1 ++ l2) = length l1 + length l2.
Proof.
  intros X l1 l2.
  induction l1.
  + reflexivity.
  + simpl. rewrite IHl1. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      rev (l1 ++ l2) = rev l2 ++ rev l1.
   ------------------------------------------------------------------ *)

Theorem rev_app_distr: forall X (l1 l2 : list X),
  rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof.
  intros X l1 l2.
  induction l1.
  + simpl. rewrite app_nil_r. reflexivity.
  + simpl. rewrite IHl1, app_assoc. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      rev (rev l) = l.
   ------------------------------------------------------------------ *)

Theorem rev_involutive : forall X : Type, forall l : list X,
  rev (rev l) = l.
Proof.
  induction l.
  + reflexivity.
  + simpl. rewrite rev_app_distr, IHl. reflexivity.
Qed.

(* =====================================================================
   §§ Polimorfismo de pares  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo prod (X Y) con el constructor pair tal que 
   (pair x y) es el par cuyas componentes son x e y.
   ------------------------------------------------------------------ *)

Inductive prod (X Y : Type) : Type :=
| pair : X -> Y -> prod X Y.

Arguments pair {X} {Y} _ _.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la abreviatura
      "( x , y )" para (pair x y).
   ------------------------------------------------------------------ *)

Notation "( x , y )" := (pair x y).

(* ---------------------------------------------------------------------
   Ejemplo. Definir la abreviatura
      "X * Y" para (prod X Y) 
   ------------------------------------------------------------------ *)

Notation "X * Y" := (prod X Y) : type_scope.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      fst {X Y : Type} (p : X * Y) : X
   tal que (fst p) es la primera componente del par p.
   ------------------------------------------------------------------ *)

Definition fst {X Y : Type} (p : X * Y) : X :=
  match p with
  | (x, y) => x
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      snd {X Y : Type} (p : X * Y) 
   tal que (snd p) es la segunda componente del par p.
   ------------------------------------------------------------------ *)

Definition snd {X Y : Type} (p : X * Y) : Y :=
  match p with
  | (x, y) => y
  end.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y) 
   tal que (combine lx ly) es la lista obtenida emparejando los
   elementos de lx y ly (como zip de Haskell).
   ------------------------------------------------------------------ *)

Fixpoint combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y) :=
  match lx, ly with
  | []     , _       => []
  | _      , []      => []
  | x :: tx, y :: ty => (x, y) :: (combine tx ty)
  end.

(* ---------------------------------------------------------------------
   Ejercicio. Calcular el resultado de 
      Check combine
   ------------------------------------------------------------------ *)

Check @combine.
(* ==> forall X Y : Type, list X -> list Y -> list (X * Y)*)

(* ---------------------------------------------------------------------
   Ejercicio. Calcular el resultado de 
      Compute (combine [1;2] [false;false;true;true]).
   ------------------------------------------------------------------ *)

Compute (combine [1;2] [false;false;true;true]).
(* ==> [(1, false); (2, false)] : list (nat * bool) *)

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y)
   tal que (split l) es el par de lista (lx,ly) cuyo emparejamiento es
   l. (La función split es como unzip de Haskell). Por ejemplo,
      split [(1,false);(2,false)] = ([1;2],[false;false]).
   ------------------------------------------------------------------ *)

Fixpoint split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y) :=
 match l with
 | []          => ([], [])
 | (x, y) :: t => let s := split t in (x :: fst s, y :: snd s)
end.

Example test_split:
  split [(1,false);(2,false)] = ([1;2],[false;false]).
Proof. reflexivity. Qed.

(* =====================================================================
   §§ Resultados opcionales polimórficos  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo (option X) con los constructores Some y None
   tales que 
   + (Some x) es un valor de tipo X.
   + None es el valor nulo.
   ------------------------------------------------------------------ *)

Inductive option (X:Type) : Type :=
  | Some : X -> option X
  | None : option X.

Arguments Some {X} _.
Arguments None {X}.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      nth_error {X : Type} (l : list X) (n : nat) : option X :=
   tal que (nth_error l n) es el n-ésimo elemento de l. Por ejemplo, 
      nth_error [4;5;6;7] 0 = Some 4.
      nth_error [[1];[2]] 1 = Some [2].
      nth_error [true] 2    = None.
   ------------------------------------------------------------------ *)

Fixpoint nth_error {X : Type} (l : list X) (n : nat) : option X :=
  match l with
  | []      => None
  | a :: l' => if beq_nat n O then Some a else nth_error l' (pred n)
  end.

Example test_nth_error1 : nth_error [4;5;6;7] 0 = Some 4.
Proof. reflexivity. Qed.
Example test_nth_error2 : nth_error [[1];[2]] 1 = Some [2].
Proof. reflexivity. Qed.
Example test_nth_error3 : nth_error [true] 2 = None.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      hd_error {X : Type} (l : list X) : option X
   tal que (hd_error l) es el primer elemento de l. Por ejemplo,
      hd_error [1;2]     = Some 1.
      hd_error [[1];[2]] = Some [1].
   ------------------------------------------------------------------ *)

Definition hd_error {X : Type} (l : list X) : option X :=
 match l with 
    | []     => None
    | x :: _ => Some x
 end.

Check @hd_error.

Example test_hd_error1 : hd_error [1;2] = Some 1.
 Proof. reflexivity. Qed.
Example test_hd_error2 : hd_error  [[1];[2]]  = Some [1].
 Proof. reflexivity. Qed.

(* =====================================================================
   § Funciones como datos
   ================================================================== *)

(* =====================================================================
   § Funciones de orden superior 
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función 
      doit3times {X:Type} (f:X->X) (n:X) : X 
   tal que (doit3times f) aplica 3 veces la función f. Por ejemplo,
      doit3times minustwo 9 = 3.
      doit3times negb true  = false.
   ------------------------------------------------------------------ *)

Definition doit3times {X:Type} (f:X->X) (n:X) : X :=
  f (f (f n)).

Check @doit3times.
(* ===> doit3times : forall X : Type, (X -> X) -> X -> X *)

Example test_doit3times: doit3times minustwo 9 = 3.
Proof. reflexivity.  Qed.
Example test_doit3times': doit3times negb true = false.
Proof. reflexivity.  Qed.

(* =====================================================================
   §§ Filtrado  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      filter {X:Type} (test: X->bool) (l:list X) : (list X)
   tal que (filter p l) es la lista de los elementos de l que verifican
   p. Por ejemplo,
      filter evenb [1;2;3;4] = [2;4].
   ------------------------------------------------------------------ *)


Fixpoint filter {X:Type} (test: X->bool) (l:list X)
                : (list X) :=
  match l with
  | []     => []
  | h :: t => if test h then h :: (filter test t)
                       else       filter test t
  end.

Example test_filter1: filter evenb [1;2;3;4] = [2;4].
Proof. reflexivity.  Qed.

Definition length_is_1 {X : Type} (l : list X) : bool :=
  beq_nat (length l) 1.

Example test_filter2:
    filter length_is_1
           [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
  = [ [3]; [4]; [8] ].
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      countoddmembers' (l:list nat) : nat 
   tal que countoddmembers' l) es el número de elementos impares de
   l. Por ejemplo,
      countoddmembers' [1;0;3;1;4;5] = 4.
      countoddmembers' [0;2;4]       = 0.
      countoddmembers' nil           = 0.
   ------------------------------------------------------------------ *)

Definition countoddmembers' (l:list nat) : nat :=
  length (filter oddb l).

Example test_countoddmembers'1:   countoddmembers' [1;0;3;1;4;5] = 4.
Proof. reflexivity.  Qed.
Example test_countoddmembers'2:   countoddmembers' [0;2;4] = 0.
Proof. reflexivity.  Qed.
Example test_countoddmembers'3:   countoddmembers' nil = 0.
Proof. reflexivity.  Qed.

(* =====================================================================
   §§ Funciones anónimas  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
      doit3times (fun n => n * n) 2 = 256.
   ------------------------------------------------------------------ *)

Example test_anon_fun':
  doit3times (fun n => n * n) 2 = 256.
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular
      filter (fun l => beq_nat (length l) 1)
             [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
   ------------------------------------------------------------------ *)

Example test_filter2':
    filter (fun l => beq_nat (length l) 1)
           [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
  = [ [3]; [4]; [8] ].
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      filter_even_gt7 (l : list nat) : list nat
   tal que (filter_even_gt7 l) es la lista de los elemntos de l que son
   pares y mayores que 7. Por ejemplo,
      filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
      filter_even_gt7 [5;2;6;19;129]      = [].
   ------------------------------------------------------------------ *)

Definition filter_even_gt7 (l : list nat) : list nat :=
  filter (fun x => evenb x && leb 7 x) l.

Example test_filter_even_gt7_1 :
  filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
 Proof. reflexivity. Qed.

Example test_filter_even_gt7_2 :
  filter_even_gt7 [5;2;6;19;129] = [].
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      partition : forall X : Type,
                  (X -> bool) -> list X -> list X * list X
   tal que (patition p l) es el par de lista (lx,ly) tal que lx es la
   lista de los elementos de l que cumplen p y ly la de las que no lo
   cumplen. Por ejemplo,
      partition oddb [1;2;3;4;5]         = ([1;3;5], [2;4]).
      partition (fun x => false) [5;9;0] = ([], [5;9;0]).
   ------------------------------------------------------------------ *)

Definition partition {X : Type}
                     (test : X -> bool)
                     (l : list X)
                   : list X * list X :=
  (filter test l, filter (fun x => negb (test x)) l).

Example test_partition1: partition oddb [1;2;3;4;5] = ([1;3;5], [2;4]).
Proof. reflexivity. Qed.
Example test_partition2: partition (fun x => false) [5;9;0] = ([], [5;9;0]).
Proof. reflexivity. Qed.

(* =====================================================================
   §§ Aplicación a todos los elementos (map)
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      map {X Y:Type} (f:X->Y) (l:list X) : (list Y) 
   tal que (map f l) es la lista obtenida aplicando f a todos los
   elementos de l. Por ejemplo,
      map (fun x => plus 3 x) [2;0;2] = [5;3;5].
      map oddb [2;1;2;5] = [false;true;false;true].
      map (fun n => [evenb n;oddb n]) [2;1;2;5]
        = [[true;false];[false;true];[true;false];[false;true]].
   ------------------------------------------------------------------ *)

Fixpoint map {X Y:Type} (f:X->Y) (l:list X) : (list Y) :=
  match l with
  | []     => []
  | h :: t => (f h) :: (map f t)
  end.

Example test_map1: map (fun x => plus 3 x) [2;0;2] = [5;3;5].
Proof. reflexivity.  Qed.

Example test_map2:
  map oddb [2;1;2;5] = [false;true;false;true].
Proof. reflexivity.  Qed.

Example test_map3:
    map (fun n => [evenb n;oddb n]) [2;1;2;5]
  = [[true;false];[false;true];[true;false];[false;true]].
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      map f (rev l) = rev (map f l).
   ------------------------------------------------------------------ *)

Lemma map_app_distr : forall (X Y : Type) (f : X -> Y) (l t : list X),
    map f (l ++ t) = map f l ++ map f t.
Proof.
  intros X Y f l t.
  induction l.
  + reflexivity.
  + simpl. rewrite IHl. reflexivity.
Qed.

Theorem map_rev : forall (X Y : Type) (f : X -> Y) (l : list X),
  map f (rev l) = rev (map f l).
Proof.
  intros X Y f l.
  induction l.
  + reflexivity.
  + simpl. rewrite map_app_distr, IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      flat_map {X Y:Type} (f:X -> list Y) (l:list X) : (list Y)
   tal que (flat_map f l) es la concatenación de las listas obtenidas
   aplicando f a l. Por ejemplo,
      flat_map (fun n => [n;n;n]) [1;5;4] = [1; 1; 1; 5; 5; 5; 4; 4; 4].
   ------------------------------------------------------------------ *)

Fixpoint flat_map {X Y:Type} (f:X -> list Y) (l:list X) 
                   : (list Y) :=
   match l with
  | [] => []
  | x :: t => f x ++ flat_map f t
  end.

Example test_flat_map1:
  flat_map (fun n => [n;n;n]) [1;5;4]
  = [1; 1; 1; 5; 5; 5; 4; 4; 4].
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      option_map {X Y : Type} (f : X -> Y) (xo : option X) : option Y
   tal que (option_map f xo) es la aplicación de f a xo.
   ------------------------------------------------------------------ *)

Definition option_map {X Y : Type} (f : X -> Y) (xo : option X)
                      : option Y :=
  match xo with
    | None   => None
    | Some x => Some (f x)
  end.

(* =====================================================================
   §§ Plegados (fold)  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      fold {X Y:Type} (f: X->Y->Y) (l:list X) (b:Y) : Y
   tal que (fold f l b) es el plegado de l con la operación f a partir
   del elemento b. Por ejemplo,
      fold mult [1;2;3;4] 1                 = 24.
      fold andb [true;true;false;true] true = false.
      fold app  [[1];[];[2;3];[4]] []       = [1;2;3;4].
   ------------------------------------------------------------------ *)

Fixpoint fold {X Y:Type} (f: X->Y->Y) (l:list X) (b:Y)
                         : Y :=
  match l with
  | nil    => b
  | h :: t => f h (fold f t b)
  end.

Check (fold andb).
(* ===> fold andb : list bool -> bool -> bool *)

Example fold_example1 :
  fold mult [1;2;3;4] 1 = 24.
Proof. reflexivity. Qed.

Example fold_example2 :
  fold andb [true;true;false;true] true = false.
Proof. reflexivity. Qed.

Example fold_example3 :
  fold app  [[1];[];[2;3];[4]] [] = [1;2;3;4].
Proof. reflexivity. Qed.

(* =====================================================================
   §§ Funciones que construyen funciones  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      constfun {X: Type} (x: X) : nat->X
   tal que (constfun x) es la función que a todos los naturales le
   asigna el x. Por ejemplo, si se define 
      Definition ftrue := constfun true.
   entonces,
      ftrue 0         = true.
      (constfun 5) 99 = 5.
   ------------------------------------------------------------------ *)

Definition constfun {X: Type} (x: X) : nat->X :=
  fun (k:nat) => x.

Definition ftrue := constfun true.

Example constfun_example1 : ftrue 0 = true.
Proof. reflexivity. Qed.

Example constfun_example2 : (constfun 5) 99 = 5.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de plus.
   ------------------------------------------------------------------ *)

Check plus.
(* ==> nat -> nat -> nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      plus3 : nat -> nat
   tal que (plus3 x) es tres más x. Por ejemplo,
      plus3 4               = 7.
      doit3times plus3 0    = 9.
      doit3times (plus 3) 0 = 9.
   ------------------------------------------------------------------ *)

Definition plus3 := plus 3.

Example test_plus3 :    plus3 4 = 7.
Proof. reflexivity.  Qed.
Example test_plus3' :   doit3times plus3 0 = 9.
Proof. reflexivity.  Qed.
Example test_plus3'' :  doit3times (plus 3) 0 = 9.
Proof. reflexivity.  Qed.

(* =====================================================================
   § Ejercicios 
   ================================================================== *)

Module Exercises.

(* ---------------------------------------------------------------------
   Ejercicio. Definir, usando fold, la función
      fold_length {X : Type} (l : list X) : nat
   tal que (fold_length l) es la longitud de l. Por ejemplo,
      fold_length [4;7;0] = 3.
   ------------------------------------------------------------------ *)
  
Definition fold_length {X : Type} (l : list X) : nat :=
  fold (fun _ n => S n) l 0.

Example test_fold_length1 : fold_length [4;7;0] = 3.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
      fold_length l = length l.
   ------------------------------------------------------------------ *)

Theorem fold_length_correct : forall X (l : list X),
  fold_length l = length l.
Proof.
  intros X l.
  unfold fold_length.
  induction l.
  - reflexivity.
  - simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir, usando fold, la función
      fold_map {X Y:Type} (f : X -> Y) (l : list X) : list Y
   tal que (fold_map f l) es la lista obtenida aplicando f a los
   elementos de l.
   ------------------------------------------------------------------ *)

Definition fold_map {X Y:Type} (f : X -> Y) (l : list X) : list Y :=
   fold (fun x t => (f x) :: t)  l [].


(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que fold_map es equivalente a map.
   ------------------------------------------------------------------ *)

Theorem fold_map_correct : forall (X Y : Type) (f : X -> Y) (l : list X),
     fold_map f l = map f l.
Proof.
  intros X Y f l.
  unfold fold_map.
  induction l.
  - reflexivity.
  - simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      prod_curry {X Y Z : Type} (f : X * Y -> Z) (x : X) (y : Y) : Z
   tal que (prod_curry f x y) es la versión curryficada de f.
   ------------------------------------------------------------------ *)

Definition prod_curry {X Y Z : Type}
  (f : X * Y -> Z) (x : X) (y : Y) : Z := f (x, y).

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      prod_uncurry {X Y Z : Type} (f : X -> Y -> Z) (p : X * Y) : Z
   tal que (prod_uncurry f p) es la versión incurryficada de f.
   ------------------------------------------------------------------ *)

Definition prod_uncurry {X Y Z : Type}
  (f : X -> Y -> Z) (p : X * Y) : Z := f (fst p) (snd p).

Check @prod_curry.
Check @prod_uncurry.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      prod_curry (prod_uncurry f) x y = f x y
   ------------------------------------------------------------------ *)

Theorem uncurry_curry : forall (X Y Z : Type)
                        (f : X -> Y -> Z)
                        x y,
  prod_curry (prod_uncurry f) x y = f x y.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      prod_uncurry (prod_curry f) p = f p.
   ------------------------------------------------------------------ *)

Theorem curry_uncurry : forall (X Y Z : Type)
                        (f : (X * Y) -> Z) (p : X * Y),
  prod_uncurry (prod_curry f) p = f p.
Proof.
  intros.
  destruct p.
  reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      forall X n l, length l = n -> @nth_error X l n = None
   ------------------------------------------------------------------ *)

(* ---------------------------------------------------------------------
   Ejercicio. En los siguientes ejercicios se trabajará con la
   definición de Church de los números naturales: el número natural n es
   la función que toma como argumento una función f y devuelve como
   valor la aplicación de n veces la función f. 
   ------------------------------------------------------------------ *)

Module Church.

(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo nat para los números naturales de Church. 
   ------------------------------------------------------------------ *)
  
Definition nat := forall X : Type, (X -> X) -> X -> X.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      one : nat
   tal que one es el número uno de Church.
   ------------------------------------------------------------------ *)

Definition one : nat :=
  fun (X : Type) (f : X -> X) (x : X) => f x.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      two : nat
   tal que two es el número dos de Church.
   ------------------------------------------------------------------ *)

Definition two : nat :=
  fun (X : Type) (f : X -> X) (x : X) => f (f x).

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      zero : nat
   tal que zero es el número cero de Church.
   ------------------------------------------------------------------ *)

Definition zero : nat :=
  fun (X : Type) (f : X -> X) (x : X) => x.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      three : nat
   tal que three es el número tres de Church.
   ------------------------------------------------------------------ *)

Definition three : nat := @doit3times.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      succ (n : nat) : nat
   tal que (succ n) es el siguiente del número n de Church. Por ejemplo, 
      succ zero = one.
      succ one  = two.
      succ two  = three.
   ------------------------------------------------------------------ *)

Definition succ (n : nat) : nat :=
   fun (X : Type) (f : X -> X) (x : X) => f (n X f x).

Example succ_1 : succ zero = one.
Proof. reflexivity. Qed.

Example succ_2 : succ one = two.
Proof. reflexivity. Qed.

Example succ_3 : succ two = three.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      plus (n m : nat) : nat
   tal que (plus n m) es la suma de n y m. Por ejemplo,
      plus zero one             = one.
      plus two three            = plus three two.
      plus (plus two two) three = plus one (plus three three).
   ------------------------------------------------------------------ *)

Definition plus (n m : nat) : nat :=
  fun (X : Type) (f : X -> X) (x : X) => m X f (n X f x).

Example plus_1 : plus zero one = one.
Proof. reflexivity. Qed.

Example plus_2 : plus two three = plus three two.
Proof. reflexivity. Qed.

Example plus_3 :
  plus (plus two two) three = plus one (plus three three).
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      mult (n m : nat) : nat
   tal que (mult n m) es el producto de n y m. Por ejemplo,
      mult one one = one.
      mult zero (plus three three) = zero.
      mult two three = plus three three.
   ------------------------------------------------------------------ *)

Definition mult (n m : nat) : nat :=
   fun (X : Type) (f : X -> X) (x : X) => n X (m X f) x.

Example mult_1 : mult one one = one.
Proof. reflexivity. Qed.

Example mult_2 : mult zero (plus three three) = zero.
Proof. reflexivity. Qed.

Example mult_3 : mult two three = plus three three.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      exp (n m : nat) : nat
   tal que (exp n m) es la potencia m-ésima de n. Por ejemplo, 
      exp two two = plus two two.
      exp three two = plus (mult two (mult two two)) one.
      exp three zero = one.
   ------------------------------------------------------------------ *)

Definition exp (n m : nat) : nat :=
  ( fun (X : Type) (f : X -> X) (x : X) => (m (X -> X) (n X) f) x).

Example exp_1 : exp two two = plus two two.
Proof. reflexivity. Qed.

Example exp_2 : exp three two = plus (mult two (mult two two)) one.
Proof. reflexivity. Qed.

Example exp_3 : exp three zero = one.
Proof. reflexivity. Qed.

End Church.

End Exercises.