附加练习 Additional Exercises

57: 2 stars, standard (fold_length)

列表上的许多常见功能可以按折叠方式实现。例如,以下是长度的替代定义:

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.

证明折叠长度的正确性。(提示:了解自反性比siml更积极地简化表达式可能会有所帮助——也就是说,您可能会发现siml什么都不做,但自反性解决了目标。)

Theorem fold_length_correct : forall X (l : list X),
  fold_length l = length l.
Proof.
(* FILL IN HERE *) Admitted.

58: 3 stars, standard (fold_map)

我们也可以用fold来定义map。完成下面的fold_map。

Definition fold_map {X Y: Type} (f: X -> Y) (l: list X) : list Y
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.

写下一个定理fold_map_correct,说明fold_map是正确的,并证明它。(提示:再一次,请记住,自反性比siml更积极地简化表达式。)

(* FILL IN HERE *)

(* Do not modify the following line: *)
Definition manual_grade_for_fold_map : option (nat*string) := None.

59: 2 stars, advanced (currying)

在Coq中,函数f:a→ B→ C真的有 A → (B → C)类型 。也就是说,如果你给f一个类型为a的值,它会给你函数f':B→ C.如果你给f'一个类型为B的值,它将返回类型为C的值。这允许部分应用,如plus3。为纪念逻辑学家Haskell Curry,使用返回函数的函数处理参数列表被称为Curry。

相反,我们可以重新解释A→ B→ C类型为(A × B) → C 这叫做uncurrying。对于无载波二进制函数,两个参数必须同时成对给出;没有部分应用程序。

我们可以对currying的定义如下:

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

作为一种练习,定义它的相反方向,即“prod_uncurry”。然后证明下面的定理,证明这两个定理是相反的。

Definition prod_uncurry {X Y Z : Type}
  (f : X -> Y -> Z) (p : X * Y) : Z
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.

作为currying有用性的一个(微不足道的)例子,我们可以用它来缩短我们上面看到的一个例子:

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

思考练习:在运行以下命令之前,您能否计算prod_curry和prod_uncurry的类型?

Check @prod_curry.
Check @prod_uncurry.

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.
  (* FILL IN HERE *) Admitted.

Theorem curry_uncurry : forall (X Y Z : Type)
                        (f : (X * Y) -> Z) (p : X * Y),
  prod_uncurry (prod_curry f) p = f p.
Proof.
  (* FILL IN HERE *) Admitted.

60: 2 stars, advanced (nth_error_informal)

回想一下 nth_error函数的定义:

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

写出以下定理的非正式证明:

forall X l n, length l = n -> @nth_error X l n = None

(* FILL IN HERE *)
(* Do not modify the following line: *)
Definition manual_grade_for_informal_proof : option (nat*string) := None.

下面的练习探索定义自然数的另一种方法,使用以数学家阿隆佐·丘奇命名的所谓丘奇数字。我们可以将一个自然数n表示为一个函数,该函数将函数f作为参数,并返回迭代n次的f。

Module Church.
Definition cnat := forall X : Type, (X -> X) -> X -> X.

让我们看看如何用这个符号写一些数字。对函数进行一次迭代应该与仅应用它相同。因此:

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

同样地,[two]应该对其论点应用[f]两次:

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

定义[zero]有点棘手:我们如何“应用函数零次”?答案其实很简单:只需原封不动地返回参数。

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

更一般地说,数字n可以写成fun X f x ⇒ f (f ... (f x) ...),其中f出现n次。请特别注意,我们前面定义的doit3times函数实际上只是3的教堂表示。

Definition three : cnat := @doit3times.

完成以下函数的定义。通过自反性证明相应的单元测试,确保它们通过。

61: 1 star, advanced (church_succ)

自然数的后继数:给定一个教会数字n,后继数succ n是一个将其参数迭代一次超过n的函数。

Definition succ (n : cnat) : cnat
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.

Example succ_1 : succ zero = one.
Proof. (* FILL IN HERE *) Admitted.

Example succ_2 : succ one = two.
Proof. (* FILL IN HERE *) Admitted.

Example succ_3 : succ two = three.
Proof. (* FILL IN HERE *) Admitted.

62: 1 star, advanced (church_plus)

两个自然数相加:

Definition plus (n m : cnat) : cnat
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.

Example plus_1 : plus zero one = one.
Proof. (* FILL IN HERE *) Admitted.

Example plus_2 : plus two three = plus three two.
Proof. (* FILL IN HERE *) Admitted.

Example plus_3 :
  plus (plus two two) three = plus one (plus three three).
Proof. (* FILL IN HERE *) Admitted.

63: 2 stars, advanced (church_mult)

乘法:

Definition mult (n m : cnat) : cnat
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.

Example mult_1 : mult one one = one.
Proof. (* FILL IN HERE *) Admitted.

Example mult_2 : mult zero (plus three three) = zero.
Proof. (* FILL IN HERE *) Admitted.

Example mult_3 : mult two three = plus three three.
Proof. (* FILL IN HERE *) Admitted.

64: 2 stars, advanced (church_exp)

指数化:

(提示:多态性在这里起着至关重要的作用。但是,选择正确的类型进行迭代可能很棘手。如果遇到“宇宙不一致”错误,请尝试迭代其他类型。迭代cnat本身通常是有问题的。)

Definition exp (n m : cnat) : cnat
  (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.

Example exp_1 : exp two two = plus two two.
Proof. (* FILL IN HERE *) Admitted.

Example exp_2 : exp three zero = one.
Proof. (* FILL IN HERE *) Admitted.

Example exp_3 : exp three two = plus (mult two (mult two two)) one.
Proof. (* FILL IN HERE *) Admitted.