【Coq】04 Basics IV 案例分析证明

案例分析证明 Proof by Case Analysis

中止 Abort

当然,并不是所有的东西都能通过简单的计算和重写来证明:一般来说,未知的假设值(任意数字、布尔值、列表等)可以阻止简化。例如,如果我们试图用上面的 simpl 策略来证明以下事实,我们就会陷入困境。(然后,我们使用 Abort 命令暂时放弃它。)

Theorem plus_1_neq_0_firsttry

Theorem plus_1_neq_0_firsttry : forall n : nat,
  (n + 1) =? 0 = false.
Proof.
  intros n.
  simpl.  (* does nothing! *)
Abort.

原因是 eqb 和 + 的定义都是从第一个参数上执行 match 开始的。但在这里,第一个参数到 + 是未知数字 n 和 eqb 的参数是复合表示 n + 1 ;两者都不能简化。

Proof.
1 subgoal
______________________________________(1/1)
forall n : nat, (n + 1 =? 0) = false
intros n.
1 subgoal
n : nat
______________________________________(1/1)
(n + 1 =? 0) = false

析构 Destruct

为了取得进展,我们需要单独考虑可能的 n 形式。如果 n 是 O ,那么我们可以计算 (n + 1) =? 0 的最终结果,并检查它是否确实是 false 的。如果对某个 n' 有 n = S n',那么,虽然我们不知道 n + 1 的确切代表什么,但我们可以计算,至少,它将从一个S开始,这足以再次计算某个 (n + 1) =? 0 将产生虚假的。

告诉 Coq 分别考虑 n = O和 n = S n' 称为 destruct 的案例的策略。

Theorem plus_1_neq_0

Theorem plus_1_neq_0 : forall n : nat,
  (n + 1) =? 0 = false.
Proof.
  intros n. destruct n as [| n'] eqn:E.
  - simpl. reflexivity.
  - simpl. reflexivity.   Qed.
destruct n as [| n'] eqn:E.
2 subgoals
n : nat
E : n = 0
______________________________________(1/2)
(0 + 1 =? 0) = false
______________________________________(2/2)
(S n' + 1 =? 0) = false
-
1 subgoal
n : nat
E : n = 0
______________________________________(1/1)
(0 + 1 =? 0) = false
simpl.
1 subgoal
n : nat
E : n = 0
______________________________________(1/1)
false = false
reflexivity.
This subproof is complete, but there are some unfocused goals:

______________________________________(1/1)
(S n' + 1 =? 0) = false
-
1 subgoal
n, n' : nat
E : n = S n'
______________________________________(1/1)
(S n' + 1 =? 0) = false
simpl.
1 subgoal
n, n' : nat
E : n = S n'
______________________________________(1/1)
false = false

析构函数生成两个子目标,然后我们必须分别证明这两个子目标,以使Coq接受该定理。

intro 模式 as[|n']

注释“as[|n']”称为 intro 模式。它告诉Coq在每个子目标中引入哪些变量名。一般来说,方括号之间是一个名称列表,用|分隔。在本例中,第一个组件是空的,因为O构造函数是空的(它没有任何参数)。第二个组件给出了一个名称 n',因为 S 是一元构造函数。

eqn:E 注释

在每个子目标中,Coq 记住与该子目标相关的关于 n 的假设——对于某些 n,n = 0 或 n = s n'。eqn:E注释告诉destruct给这个方程命名为E。省略eqn:E注释会导致Coq在子目标中省略这些假设。这稍微简化了没有明确使用假设的证明,但是为了文档的目的保留它们是更好的做法,因为它们可以帮助您在处理子目标时保持方向性。

- 符号 bullets

第二行和第三行上的-符号称为项目符号,它们标记了与生成的两个子目标相对应的证明部分。 - 后面的证明脚本部分是对应子目标的完整证明。在这个例子中,每个子目标都很容易通过一次使用自反性来证明,自反性本身执行一些简化——例如,第二个通过先将(s n'+1)重写为S(n'+1),然后展开eqb简化了(s n'+1)=?0 到 false。

用项目符号标记案例是可选的:如果项目符号不存在,Coq只要求您按顺序证明每个子目标,一次一个。但使用项目符号是个好主意。一方面,它们使证明的结构更加明显,提高了可读性。此外,项目符号指示Coq在尝试验证下一个子目标之前确保子目标已完成,以防止不同子目标的证明混淆。这些问题在大型开发中变得尤为重要,因为脆弱的证明会导致长时间的调试会话。

对于如何在Coq中格式化校样,并没有硬性规定——例如,在哪里应该断行,以及校样的各个部分应该如何缩进以指示其嵌套结构。但是,如果生成多个子目标的位置在行的开头用明确的项目符号标记,那么无论对布局的其他方面做了什么选择,证明都是可读的。

这也是一个很好的地方来提及另一条关于线路长度的明显建议。刚开始使用Coq的用户有时会走向极端,要么在自己的一行上写下每种策略,要么在一行上写下整个证明。好的风格就在中间。一个合理的准则是将自己限制在80个字符的行内。

destruct 策略可以用于任何归纳定义的数据类型。例如,我们接下来用它来证明布尔否定是对合的——也就是说,否定是它自己的逆。

Theorem negb_involutive

Theorem negb_involutive : forall b : bool,
  negb (negb b) = b.
Proof.
  intros b. destruct b eqn:E.
  - reflexivity.
  - reflexivity.  Qed.

请注意,此处的 destruct 没有 as 子句,因为 destruct 的子类不需要绑定任何变量,因此无需指定任何名称。事实上,我们可以从任何 destruct 中省略 as 子句,Coq 将自动填写变量名。这通常被认为是糟糕的风格,因为 Coq 经常在留给自己的设备时做出令人困惑的名称选择。

有时在子目标内调用 destruct 是有用的,从而产生更多的证明义务。在这种情况下,我们使用不同类型的项目符号在不同的"级别"上标记目标。例如:

Theorem andb_commutative 项目符号

Theorem andb_commutative : forall b c, andb b c = andb c b.
Proof.
  intros b c. destruct b eqn:Eb.
  - destruct c eqn:Ec.
    + reflexivity.
    + reflexivity.
  - destruct c eqn:Ec.
    + reflexivity.
    + reflexivity.
Qed.

对 reflexivity 的每一对调用都对应于在其正上方执行 destruct c 后生成的子目标。

除了 - 和 + ,我们还可以使用 ×(星号)或任何重复的项目符号(例如,-- 或 *** )作为项目符号。我们也可以在花键中附上子证明:

Theorem andb_commutative' 花键

Theorem andb_commutative' : forall b c, andb b c = andb c b.
Proof.
  intros b c. destruct b eqn:Eb.
  { destruct c eqn:Ec.
    { reflexivity. }
    { reflexivity. } }
  { destruct c eqn:Ec.
    { reflexivity. }
    { reflexivity. } }
Qed.

由于大括号标记了证明的开始和结束,因此它们可用于多个子目标级别,如本示例所示。此外,大括号允许我们在多个级别上重复使用相同的项目符号作为证明。大括号、项目符号或两者的组合的选择纯粹是品味问题。

Theorem andb3_exchange 花键和项目符号的组合

Theorem andb3_exchange :
  forall b c d, andb (andb b c) d = andb (andb b d) c.
Proof.
  intros b c d. destruct b eqn:Eb.
  - destruct c eqn:Ec.
    { destruct d eqn:Ed.
      - reflexivity.
      - reflexivity. }
    { destruct d eqn:Ed.
      - reflexivity.
      - reflexivity. }
  - destruct c eqn:Ec.
    { destruct d eqn:Ed.
      - reflexivity.
      - reflexivity. }
    { destruct d eqn:Ed.
      - reflexivity.
      - reflexivity. }
Qed.

练习 Exercise

7: 2 stars, standard (andb_true_elim2)

证明以下声明,在使用 destruct 时用项目符号标记案例(和子案例)。提示:延迟引入假设,直到你有机会简化它。

Theorem andb_true_elim2 : forall b c : bool,
  andb b c = true -> c = true.
Proof.
  (* FILL IN HERE *) Admitted.

Coq代码

Proof.
intros b c . destruct b eqn:Eb.
  - destruct c eqn:Ec. 
    + intros A. reflexivity.
    + intros B. rewrite <- B. reflexivity.
  - destruct c eqn:Ec. 
    + intros C. reflexivity.
    + intros D. rewrite <- D. reflexivity.

  Qed.

Coq运行截图

alt

8: 1 star, standard (zero_nbeq_plus_1)

最后的便利 Final Convenience

在结束这一章之前,让我们来提一下最后的便利。正如你可能已经注意到的,许多证据在介绍后立即对一个变量进行案例分析:

intros x y. destruct y as [|y] eqn:E.

此模式非常常见,Coq 为它提供了速记:在引入变量时,我们可以使用介绍模式而不是可变名称对变量进行案例分析。例如,下面是上述 plus_1_neq_0 定理的较短证明。(您还会注意到此速记的一个缺点:我们丢失了记录我们在每个子行中做出的假设的方程,我们以前从 eqn:E 注释中获得了该方程。

Theorem plus_1_neq_0'

Theorem plus_1_neq_0' : forall n : nat,
  (n + 1) =? 0 = false.
Proof.
  intros [|n].
  - reflexivity.
  - reflexivity.  Qed.
intros [|n].
2 subgoals
______________________________________(1/2)
(0 + 1 =? 0) = false
______________________________________(2/2)
(S n + 1 =? 0) = false
-
1 subgoal
______________________________________(1/1)
(0 + 1 =? 0) = false

simpl.
1 subgoal
______________________________________(1/1)
false = false

Theorem andb_commutative''

如果没有需要名称的构造参数,我们可以只编写[]来获取案例分析。

Theorem andb_commutative'' :
  forall b c, andb b c = andb c b.
Proof.
  intros [] [].
  - reflexivity.
  - reflexivity.
  - reflexivity.
  - reflexivity.
Qed.
intros [] [].
4 subgoals
______________________________________(1/4)
true && true = true && true
______________________________________(2/4)
true && false = false && true
______________________________________(3/4)
false && true = true && false
______________________________________(4/4)
false && false = false && false

Coq题目

Theorem zero_nbeq_plus_1 : forall n : nat,
  0 =? (n + 1) = false.
Proof.
  (* FILL IN HERE *) Admitted.

Coq代码

Proof.
intros [].
  - reflexivity.
  - reflexivity.
Qed.

Coq运行截图

alt

【学习】函数语言程序设计 文章被收录于专栏

函数语言程序设计 Coq章节 Ch1 Basics【COQ中的函数编程】 Ch2 Induction【归纳法证明】 Ch3 Lists【使用结构化数据】 Ch4 Poly【多态性与高阶函数】 Ch5 Tactics【更基本的战术】 Ch6 Logic【COQ中的逻辑】 Ch7 IndProp【归纳定义命题】 Ch8 Maps【全图和局部图】 Ch9 Rel【关系的性质】

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务