单独编译 Separate Compilation

在开始本章之前,我们需要从上一章中导入我们的所有定义:

From Coq Require Export String.

Fixpoint even (n:nat) : bool :=
  match n with
  | O        => true
  | S O      => false
  | S (S n') => even n'
  end.

Fixpoint leb (n m : nat) : bool :=
  match n with
  | O => true
  | S n' =>
      match m with
      | O => false
      | S m' => leb n' m'
      end
  end.

归纳法证明 Proof by Induction

我们可以用自反性证明0是+左边的中性元素。但证明它也是右边的中性元素的证据。。。

Theorem add_0_r_firsttry : ∀ n:nat,
  n + 0 = n.

不能用同样简单的方法来做。仅仅应用自反性是行不通的,因为n+0中的n是一个任意的未知数,所以不能简化+定义中的匹配。

Proof.
  intros n.
  simpl. (* Does nothing! *)
Abort.

使用析构函数n进行案例推理并没有让我们走得更远:我们假设 n = 0 的案例分析分支可以顺利通过,但在 n = S n' 的分支中,对于某些n'我们会以完全相同的方式陷入困境。

Theorem add_0_r_secondtry : ∀ n:nat,
  n + 0 = n.
Proof.
  intros n. destruct n as [| n'] eqn:E.
  - (* n = 0 *)
    reflexivity. (* so far so good... *)
  - (* n = S n' *)
    simpl. (* ...but here we are stuck again *)
Abort.

我们可以使用析构函数n'更进一步,但是,因为n可以任意大,如果我们继续这样下去,我们永远不会得到所有的结果。

为了证明有关数字、列表和其他归纳定义集的有趣事实,我们通常需要一个更强大的推理原则:归纳。

回想一下(高中,离散数学课程等)自然数归纳的原理:如果P(n)是一个涉及自然数n的命题,我们想证明P对所有数字n都成立,我们可以这样推理:

  • 证明P(O)成立;
  • 证明,对于任何n',如果P(n')成立,那么P(S n')也成立;
  • 得出P(n)适用于所有n的结论。

在Coq中,步骤是相同的:我们从证明所有n的P(n)的目标开始,并将其分解为两个单独的子目标(通过应用归纳策略):一个是我们必须显示P(O),另一个是我们必须显示P(n')→ P(sn')。下面是这个定理的工作原理:

Theorem add_0_r : ∀ n:nat, n + 0 = n.
Proof.
  intros n. induction n as [| n' IHn'].
  - (* n = 0 *) reflexivity.
  - (* n = S n' *) simpl. rewrite → IHn'. reflexivity. Qed.

就像析构函数一样,归纳函数也需要一个as……子句,指定要在子目标中引入的变量的名称。由于有两个子目标,“as…”子句有两部分,由|分隔。(严格地说,我们可以省略as…子句,Coq将为我们选择名称。实际上,这是一个坏主意,因为Coq的自动选择往往令人困惑。)

在第一个子目标中,n被0替换。没有引入新变量(因此as…的第一部分为空),目标变为0=0+0,随后进行简化。

在第二个子目标中,n被sn'替换,假设n'+0=n'被添加到上下文中,并命名为IHn'(即,n'的归纳假设)。这两个名称在as。。。条款本例中的目标是sn'=(sn')+0,这将简化为sn'=S(n'+0),然后再从IHn'开始。

Theorem minus_n_n : ∀ n,
  minus n n = 0.
Proof.
  (* WORKED IN CLASS *)
  intros n. induction n as [| n' IHn'].
  - (* n = 0 *)
    simpl. reflexivity.
  - (* n = S n' *)
    simpl. rewrite → IHn'. reflexivity. Qed.

(在这些证明中使用介绍策略实际上是多余的。当应用于包含量化变量的目标时,归纳策略将根据需要自动将其移动到上下文中。)

14: 2 stars, standard, especially useful (basic_induction)

使用归纳法证明以下内容。您可能需要以前验证过的结果。

Theorem mul_0_r

Theorem mul_0_r : ∀ n:nat,
  n × 0 = 0.
Proof.
  (* FILL IN HERE *) Admitted.
Coq代码
Theorem mul_0_r : forall n:nat,
n * 0 = 0.
Proof.
intros n. induction n as [| n' IHn'].
- (* n = 0 *)
  simpl. reflexivity.
- (* n = S n' *)
  simpl. rewrite -> IHn'. reflexivity.  Qed.

Theorem plus_n_Sm

Theorem plus_n_Sm : ∀ n m : nat,
S (n + m) = n + (S m).
Proof.
(* FILL IN HERE *) Admitted.
Coq代码
Theorem plus_n_Sm : forall n m : nat,
S (n + m) = n + (S m).
Proof.
intros n. induction n as [| n' IHn'].
- intros m. induction m as [| m' IHm'].
  -- simpl. reflexivity.
  -- simpl. reflexivity.
- intros m. induction m as [| m' IHm'].
  -- simpl. rewrite -> IHn'. reflexivity. 
  -- simpl. rewrite -> IHn'. reflexivity. Qed.

Theorem add_comm

Theorem add_comm : ∀ n m : nat,
n + m = m + n.
Proof.
(* FILL IN HERE *) Admitted.
Coq代码
Theorem add_comm : forall n m : nat,
n + m = m + n.
Proof.
induction n as [| n' IHn'].
- induction m as [| m' IHm'].
  -- simpl. reflexivity.
  -- simpl. rewrite <- IHm'. simpl. reflexivity.
- induction m as [| m' IHm']. 
  -- simpl. rewrite -> IHn'. simpl. reflexivity.
  -- simpl. rewrite <- IHm'. rewrite -> IHn'. simpl. rewrite -> IHn'. reflexivity.
Qed.

Theorem add_assoc

Theorem add_assoc : ∀ n m p : nat,
n + (m + p) = (n + m) + p.
Proof.
(* FILL IN HERE *) Admitted.
Coq代码
Theorem add_assoc : forall n m p : nat,
n + (m + p) = (n + m) + p.
Proof.
induction n as [| n' IHn'].
- induction m as [| m' IHm'].
  -- induction p as [| p' IHp'].
  	--- simpl. reflexivity.
  	--- simpl. reflexivity.
  -- induction p as [| p' IHp'].
  	--- simpl. reflexivity.
  	--- simpl. reflexivity.
- induction m as [| m' IHm'].
  -- simpl. induction p as [| p' IHp'].
  	--- rewrite <- IHn'. simpl. reflexivity.
  	--- rewrite <- IHn'. simpl. reflexivity.
  -- simpl. induction p as [| p' IHp'].
  	--- rewrite <- IHn'. simpl. reflexivity.
  	--- rewrite <- IHn'. simpl. reflexivity.
Qed.

15: 2 stars, standard (double_plus)

考虑下面的函数,它的论点加倍:

Fixpoint double (n:nat) :=
match n with
| O ⇒ O
| S n' ⇒ S (S (double n'))
end.

使用归纳法来证明关于双重的这一简单事实:

Lemma double_plus : ∀ n, double n = n + n .
Proof.
(* FILL IN HERE *) Admitted.
Coq代码
Lemma double_plus : forall n, double n = n + n .
Proof.
intros. induction n as [| n' IHn'].
- simpl. reflexivity.
- simpl. rewrite -> IHn'. rewrite -> plus_n_Sm. reflexivity.
Qed.

16: 2 stars, standard, optional (even_S)

偶数n定义的一个不方便的方面是对n-2的递归调用。这使得关于n的证明在n上进行归纳时更加困难,因为我们可能需要一个关于n-2的归纳假设。下面的引理给出了偶数(sn)的另一种特征,这种特征在归纳法中效果更好:

Theorem even_S : ∀ n : nat,
even (S n) = negb (even n).
Proof.
(* FILL IN HERE *) Admitted.
Coq代码
Theorem even_S : forall n : nat,
even (S n) = negb (even n).
Proof.
intros. induction n as [| n' IHn'].
- simpl. reflexivity.
- rewrite -> IHn'. simpl. rewrite -> negb_involutive. reflexivity.
Qed.

17: 1 star, standard, optional (destruct_induction)

简要说明析构函数和归纳的区别。

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