翻译-TPIL-04-量词与相等关系

上一章介绍了如何对含有命题逻辑连结词的断言构造证明。 这一章我们将扩充构造命题逻辑公式的命令,使其囊括 全称量词、存在量词以及相等关系。

4.1. 全称量词 The Universal Quantifier

对于任意类型 αα 上的一元谓词 p 可被视作类型为 α → Prop 的对象, 如此 x : αp x 就表示断言“px 上成立”;类似地 对象 r : α → α → Prop 代表一个 α 上的二元关系, 给定 x y : αr x y 就表示断言“xy 相关联”。

全称量词 ∀ x : α, p x 表示的断言是 “对每一个 x : α 都会有 p x 成立”。 类似命题连结词,“forall”在自然演绎系统中 也受引入和消去规则支配。非正式地, 引入规则宣称:

若已有 p x 的证明,且语境中 x : α 都是任意的, 则可得 ∀ x : α, p x 的证明。

消去规则宣称:

对任何项 t : α,若已有 ∀ x : α, p x 的证明, 则可得 p t 的证明。

正如处理蕴含时一样,“命题即类型”释义再次发挥作用。 回想一下依值箭头类型的引入和消去规则:

若已有类型为 β x 的项 t,且语境中 x : α 都是任意的, 则可得 (fun x : α => t) : (x : α) → β x

消去规则宣称:

对任何项 t : α,若已有项 s : (x : α) → β x 则可得 s t : β t

对于 p x 具有 Prop 类型的情形下, 如果将 (x : α) → β x 替换为 ∀ x : α, p x, 那么我们就可以将它们解读为 构造涉及全称量词的证明的正确规则。

因此构造演算便以这种方式将依值箭头类型与全称表达式对应起来。 如果 p 为任意表达式,那么 ∀ x : α, p 不过是 (x : α) → p 的一种替代记法罢了, 并且在 p 为命题的情况下前者比后者更自然。 通常表达式 p 会依赖于 x : α回想一下,对于普通函数空间, 我们可以将函数类型 α → β 解释为 (x : α) → β 的一种特殊情形——即 β 不依赖于 x 的情况。类似地, 我们可以将命题蕴含 p → q 看作是 ∀ x : p, q 的一种特殊情形——即 q 不依赖于 x 的情况。

下面的例子演示了“命题即类型”是如何在实际应用中发挥作用的。

1
2
3
4
example (α : Type) (p q : α  Prop) : ( x : α, p x  q x)   y : α, p y :=
  fun h :  x : α, p x  q x =>   -- 箭头引入
  fun y : α =>                    -- 全称引入
  show p y from (h y).left        -- 合取消除 全称消去

作为一种书写约定,我们赋予全称量词尽可能大的作用域。 因此对于上方例子中的假设我们需要用括号限定 x 的量词的作用范围; 证明 ∀ y : α, p y 的规范方法是先取任意的 y 再证明 p y,这是引入规则; 已知 h 的类型为 ∀ x : α, p x ∧ q x 则表达式 h y 的类型即为 p y ∧ q y,这是消去规则; 取合取的左侧部分便可获得所需结论 p y

请记住,如果表达式之间的差异仅在于 其中一个是对另一个里所有约束变量的重命名, 那么它们就可以被视作是等价的。因此 我们就可以在假设和结论中使用相同的变量 x, 并在证明中用其他变量(比如 z)来实例化它,见下:

1
2
3
4
example (α : Type) (p q : α  Prop) : ( x : α, p x  q x)   x : α, p x :=
  fun h :  x : α, p x  q x =>   -- 箭头引入
  fun z : α =>                    -- 全称引入
  show p z from And.left (h z)    -- 合取消除 全称消去

再举一个例子:下面展示了该 如何描述一个关系 r 具有传递性 Transitivity

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
variable (α : Type) (r : α  α  Prop)
variable (trans_r :  x y z, r x y  r y z  r x z)

variable (a b c : α)
variable (hab : r a b) (hbc : r b c)

#check trans_r
-- trans_r : ∀ (x y z : α), r x y → r y z → r x z
#check trans_r a b c
-- trans_r a b c : r a b → r b c → r a c
#check trans_r a b c hab
-- trans_r a b c hab : r b c → r a c
#check trans_r a b c hab hbc
-- trans_r a b c hab hbc : r a c

思考一下到底发生了什么。在用值 abc 实例化 trans_r 后 我们会得到 r a b → r b c → r a c 的证明;将此应用于“假设” hab : r a b 后 我们便得到 r b c → r a c 的一个证明;最后将其应用到假设 hbc 上 便会有结论 r a c 的证明。

在上例的情形中,显示地写出参数 abc 会很繁琐 ——如果它们可以从 hab hbc 中推断出来的话。 因此它们通常会被设置为隐式参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
variable (α : Type) (r : α  α  Prop)
variable (trans_r :  {x y z}, r x y  r y z  r x z)

variable (a b c : α)
variable (hab : r a b) (hbc : r b c)

#check trans_r
-- trans_r : r ?m.4 ?m.5 → r ?m.5 ?m.6 → r ?m.4 ?m.6
#check trans_r hab
-- trans_r hab : r b ?m.6 → r a ?m.6
#check trans_r hab hbc
-- trans_r hab hbc : r a c

这样做的优点是我们可以简单地写下 trans_r hab hbc 作为 r a c 的证明。 但有一个缺点:Lean 没有足够的信息来推断表达式 trans_rtrans_r hab 中参数的类型。 第一个 #check 命令的输出是 r ?m.1 ?m.2 → r ?m.2 ?m.3 → r ?m.1 ?m.3, 说明在本例中的隐式参数未被指定。

下面的例子展示了如何使用等价关系进行基本推理:

1
2
3
4
5
6
7
8
variable (α : Type) (r : α  α  Prop)

variable (refl_r :  x, r x x)
variable (symm_r :  {x y}, r x y  r y x)
variable (trans_r :  {x y z}, r x y  r y z  r x z)

example (a b c d : α) (hab : r a b) (hcb : r c b) (hcd : r c d) : r a d :=
  trans_r (trans_r hab (symm_r hcb)) hcd

要想熟悉使用全称量词, 你应该试试本节末尾的一些练习。

依值箭头类型的类型规则,尤其是全称量词, 体现了 Prop 类型与其他对象的类型的不同之处: 假设我们有 α : Sort iβ : Sort j—— 其中表达式 β 可能依赖于变量 x : α, 那么 (x : α) → β 便是 Sort (imax i j) 的一个元素, 其中 imax i jijj 不为 0 时的最大值,否则为 0。

其思路如下: 如果 j 不是 0, 那么 (x : α) → β 便是类型 Sort (max i j) 的一个元素,也就是说 类型为 αβ 的依值函数“活在”指数为 ij 两者最大值的宇宙中。然而, 假如 β 属于 Sort 0——即 Prop 的一个元素, 那么 (x : α) → β 也是 Sort 0 的一个元素—— 无论 α 生活在哪种类型的宇宙中,也就是说 如果 β 是一个依赖于 α 的命题, 那么 ∀ x : α, β 仍然是一个命题。 这反映出 Prop 被解释为一种命题的类型而非数据类型,同时 这也使得 Prop 具有非直谓性 impredicative”。

“直谓性”一词源于二十世纪初期的数学基础研究。 当时如庞加莱和罗素之类的逻辑学家 将集合论的悖论归咎于“恶性循环”——比如 某个属性是通过量化一个类来定义的, 而这个类又包含了待定义的属性。 会发现:如果 α 是任意类型, 那么我们就可以在 α 上构造类型 α → Prop 来囊括所有与 α 相关的谓词(Prop 的“幂”)。 Prop 的非直谓性意味着 我们可以通过量化 α → Prop 来形成命题。特别地, 我们可以通过量化所有关于 α 的谓词来定义 α 上的谓词, 而这正是曾被认为是有问题的循环类型。

下面是一些练习题:


1
2
3
4
5
variable (α : Type) (p q : α  Prop)

example : ( x, p x  q x)  ( x, p x)  ( x, q x) := sorry
example : ( x, p x  q x)  ( x, p x)  ( x, q x) := sorry
example : ( x, p x)  ( x, q x)   x, p x  q x := sorry
  1. (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x)

    答:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    variable 
    (α : Type) 
    (p q : α  Prop)
    (r : Prop)
    
    example : ( x, p x  q x)  ( x, p x)  ( x, q x)
      :=Iff.intro
        (λ (h :  x, p x  q x) 
        ⟨λ (x : α)  (h x).left
        ,λ (x : α)  (h x).right)
        (λ (h : ( x, p x)  ( x, q x)) 
        λ (x : α)  h.left x,(h.right x))
  2. (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x)

    答:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : ( x, p x  q x)  ( x, p x)  ( x, q x)
      :=(λ (hpq :  x, p x  q x) 
         λ (hp  :  x, p x) 
         λ (x : α) 
         (hpq x) (hp x))
  3. (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x

    答:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : ( x, p x)  ( x, q x)   x, p x  q x
      :=(λ (hpq : ( x, p x)  ( x, q x)) 
         λ (x : α)  match hpq with
         | Or.inl hp => Or.inl (hp x)
         | Or.inr hq => Or.inr (hq x))

当公式的一个组件不依赖于量化变量时,通常可以将其移到全称量词之外。 尝试证明这些(其中第二个的一个方向需要经典逻辑)

1
2
3
4
5
6
variable (α : Type) (p q : α  Prop)
variable (r : Prop)

example : α  (( x : α, r)  r) := sorry
example : ( x, p x  r)  ( x, p x)  r := sorry
example : ( x, r  p x)  (r   x, p x) := sorry
  1. α → ((∀ x : α, r) ↔ r)

    答:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : α  ( _ : α, r)  r
      : (x : α) 
        λ (h :  _ : α, r) 
        h x
  2. (∀ x, p x ∨ r) ↔ (∀ x, p x) ∨ r

    答:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    open Classical
    
    example : ( x, p x  r)  ( x, p x)  r
      :=Iff.intro
        (λ (h :  x, p x  r)  byCases
          (λ (hr  :  r)  Or.inr hr)
          (λ (hnr : ¬r)  Or.inl
            (λ (x : α)  match (h x) with
            | Or.inl hl => hl
            | Or.inr hr => absurd hr hnr)))
        (λ (h :( x, p x)  r)  match h with
        | Or.inl hl => (λ (x : α)  Or.inl (hl x))
        | Or.inr hr => (λ (_ : α)  Or.inr hr))
  3. (∀ x, r → p x) ↔ (r → ∀ x, p x)

    答:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : ( x, r  p x)  (r   x, p x)
      :=Iff.intro
        (λ (hrp :  x, r  p x) 
         λ (hr  : r)  
         λ (x : α) 
         hrp x hr)
        (λ (hrp : r   x, p x)  
         λ (x : α)  
         λ (hr  :  r) 
         hrp hr x)

考虑理发师悖论 Barber Paradox”: 小镇上有个男理发师,他只为所有不给自己刮胡子的男人刮胡子。请证明这是不可能的。

1
2
3
4
5
variable (men : Type) (barber : men)
variable (shaves : men  men  Prop)

example (h :  x : men, shaves barber x  ¬ shaves x x) : False :=
  sorry
  • 答:借用上一章第 17 题的答案。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    variable
    (α : Type)
    (a : α)
    (p : α  α  Prop)
    
    theorem cache {p : Prop} : ¬(p  ¬p)
      : (hpnp : p  ¬p) 
        let hnp : p  False := λ (hp : p)  hpnp.mp hp hp
        let hp  : p         := hpnp.mpr hnp
        hpnp.mp hp hp
    example : ( x : α, p a x  ¬p x x)  False
      : (h :  x : α, p a x  ¬p x x) 
        cache (h a)

4.2. 相等 Equality

现在来看看 Lean 库中定义的最基本的关系之一——相等关系。 在归纳类型一章我们将解释相等在 Lean 的逻辑框架中是如何被定义的。 不过在此之前先介绍一下如何使用它。

当然,相等关系的基本性质之一 便是其构成一个等价关系:

1
2
3
4
5
6
#check Eq.refl
-- Eq.refl.{u_1} {α : Sort u_1} (a : α) : a = a
#check Eq.symm
-- Eq.symm.{u} {α : Sort u} {a b : α} (h : a = b) : b = a
#check Eq.trans
-- Eq.trans.{u} {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b = c) : a = c

我们可以让输出变得更易读—— 只需告诉 Lean 不要插入隐式参数(这里显示为元变量)即可。

1
2
3
4
5
6
7
8
universe u

#check @Eq.refl.{u}
-- @Eq.refl : ∀ {α : Sort u} (a : α), a = a
#check @Eq.symm.{u}
-- @Eq.symm : ∀ {α : Sort u} {a b : α}, a = b → b = a
#check @Eq.trans.{u}
-- @Eq.trans : ∀ {α : Sort u} {a b c : α}, a = b → b = c → a = c

标记 .{u} 告诉 Lean 在宇宙 u 上实例化常量。

如此我们便可将上一节中的例子 套到相等关系上:

1
2
3
4
5
variable (α : Type) (a b c d : α)
variable (hab : a = b) (hcb : c = b) (hcd : c = d)

example : a = d :=
  Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd

我们也可以使用投影记号

1
2
3
4
5
variable (α : Type) (a b c d : α)
variable (hab : a = b) (hcb : c = b) (hcd : c = d)

example : a = d := 
  (hab.trans hcb.symm).trans hcd

自反性 Reflexivity 比表面上更强大。回想一下, 在构造演算中每个项都有一个计算释义, 而所有能被规约为相同形式的项 在逻辑框架内都会被视作是相同的。 正因如此,一些看似非平凡的恒等式 也可以通过自反性来证明:

1
2
3
4
5
variable (α β : Type)

example (f : α  β) (a : α) : (fun x => f x) a = f a := Eq.refl _
example (a : α) (b : β) : (a, b).1 = a := Eq.refl _
example : 2 + 3 = 5 := Eq.refl _

这个特性非常重要,以至于库中 为 Eq.refl _ 专门定义了一个符号 rfl

1
2
3
4
5
variable (α β : Type)

example (f : α  β) (a : α) : (fun x => f x) a = f a := rfl
example (a : α) (b : β) : (a, b).1 = a := rfl
example : 2 + 3 = 5 := rfl

然而相等不仅仅是一种等价关系,它还有一个重要的性质:每个断言都遵从等价性, 意味着可以在不改变真值的情况下对表达式做等价代换;也就是说, 给定 h1 : a = bh2 : p a 我们就可以构造一个 p b 的证明—— 使用代换 Eq.subst h1 h2 即可。

1
2
3
4
5
6
7
8
example (α : Type) (a b : α) (p : α  Prop)
        (h1 : a = b) (h2 : p a) : p b :=
  Eq.subst h1 h2
--h1.subst h2   -- 这样写也行

example (α : Type) (a b : α) (p : α  Prop)
  (h1 : a = b) (h2 : p a) : p b :=
  h1  h2

第二个例子中的而三角形 是一个 建立在 Eq.substEq.symm 之上的宏, 你可以通过输入 \t 来打出它。

规则 Eq.subst 定义了一系列辅助规则以进行更显式的替换, 它们用于处理应用项 applicative term,即形如 s t 的项;具体来说: congrArg 用于替换实参, congrFun 用于替换函数, congr 可同时替换两者。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
variable (α : Type)
variable (a b : α)
variable (f g : α  Nat)
variable (h₁ : a = b)
variable (h₂ : f = g)

example : f a = f b
  :=congrArg f h₁
--:=by rw [h₁]              -- 这样写也行 4.3 节会解释
--:=h₁ ▸ Eq.refl (f a)      -- 这样写也行 注意:这里用 subst 会报错,在高阶合一那里有解释

example : f a = g a
  :=congrFun h₂ a
--:=by rw [h₂]              -- 这样写也行
--:=h₂ ▸ Eq.refl (f a)      -- 这样写也行

example : f a = g b
  :=congr h₂ h₁
--:=by rw [h₂, h₁]          -- 这样写也行
--:=h₂ ▸ h₁ ▸ Eq.refl (f a) -- 这样写也行

Lean 的库包含大量常用等式,比如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
variable (a b c : Nat)

example : a + 0 = a := Nat.add_zero a
example : 0 + a = a := Nat.zero_add a
example : a * 1 = a := Nat.mul_one a
example : 1 * a = a := Nat.one_mul a
example : a + b = b + a := Nat.add_comm a b
example : a + b + c = a + (b + c) := Nat.add_assoc a b c
example : a * b = b * a := Nat.mul_comm a b
example : a * b * c = a * (b * c) := Nat.mul_assoc a b c
example : a * (b + c) = a * b + a * c := Nat.mul_add a b c
example : a * (b + c) = a * b + a * c := Nat.left_distrib a b c
example : (a + b) * c = a * c + b * c := Nat.add_mul a b c
example : (a + b) * c = a * c + b * c := Nat.right_distrib a b c

Nat.mul_addNat.add_mul 分别是 Nat.left_distribNat.right_distrib 的别名。 上面的属性都是针对自然数类型 Nat 的。

下面关于计算自然数的例子 使用了代换外加自然数的结合律、交换律及分配律。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
example (x y : Nat) : (x + y) * (x + y) = x * x + y * x + x * y + y * y
  :=have h1 : (x + y) * (x + y) = (x + y) * x + (x + y) * y
    :=Nat.mul_add (x + y) x y
    have h2 : (x + y) * (x + y) = x * x + y * x + (x * y + y * y)
      -- (x + y)² = x² + y² + (xy + yx)
    :=(Nat.add_mul x y x) 
      -- (x + y)² = (x² + yx) + (xy + y²) , 将 (x + y) * x 展开为 x * x + y * x
      (Nat.add_mul x y y) 
      -- (x + y)² = (x + y)x  + (xy + y²) , 将 (x + y)y 展开为 xy + y²
      h1
      -- (x + y)² = (x + y)x + (x + y)y
    h2.trans (Nat.add_assoc
        (x * x + y * x)
        (x * y)
        (y * y)
        -- 得到 x² + yx + xy + y² = x² + yx + (xy + y²)
      ).symm
      -- 得到 x² + yx + (xy + y²) = x² + yx + xy + y²
    -- 得到 (x + y)² = x² + yx + xy + y²

注意到 Eq.subst 的第二个隐式参数 提供了将要发生代换的语境,其类型为 α → Prop; 这意味着推断这个谓词需要一个 高阶合一 High-Order Unification 的实例。 一般来说高阶合一器的存在性问题是不可判定的, Lean 顶多只能提供不完美的和近似的解决方案, 因此 Eq.subst 并不总是能实现你想要的效果; 宏 h ▸ e 使用了更有效的启发式方法来计算这个隐式参数, 并且通常会在 Eq.subst 失效的情况下成功

由于等式推理是如此的普遍和重要, Lean 提供了许多机制以更有效地执行它。 下一节介绍的语法允许你以更自然和清晰的方式书写计算式证明。 但更重要的是,等式推理是由 项重写器 term rewriter、 简化器 simplifier 和 其他种类的自动化方法支持的。 项重写器和简化器将在下一节中简要描述, 而更多细节将在下一章中介绍。

4.3. 计算式证明 Calculational Proofs

计算式证明是一串中间结果, 这些结果通过如等式传递性这样的基本原理进行串联。 在 Lean 中计算式证明以关键字 calc 开始,语法如下:

1
2
3
4
5
calc
  <expr>_0  'op_1'  <expr>_1  ':='  <proof>_1
  '_'       'op_2'  <expr>_2  ':='  <proof>_2
  ...
  '_'       'op_n'  <expr>_n  ':='  <proof>_n

注意到 calc 下的每一对相等关系都使用了相同的缩进。 每个 <proof>_i 都是对 <expr>_{i-1} op_i <expr>_i 的证明。

我们也可以在第一对相等关系中使用 _(就在 <expr>_0 正后方), 如此便可将关系对/证明对对齐:

1
2
3
4
5
calc <expr>_0
  '_' 'op_1' <expr>_1 ':=' <proof>_1
  '_' 'op_2' <expr>_2 ':=' <proof>_2
  ...
  '_' 'op_n' <expr>_n ':=' <proof>_n

下面是一个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
variable (a b c d e : Nat)

variable (h1 : a = b)
variable (h2 : b = c + 1)
variable (h3 : c = d)
variable (h4 : e = 1 + d)

theorem T : a = e :=
  calc
    a = b      := h1
    _ = c + 1  := h2
    _ = d + 1  := congrArg Nat.succ h3
    _ = 1 + d  := Nat.add_comm d 1
    _ = e      := Eq.symm h4

这种书写证明的风格在与 simprewrite 策略结合使用时最为有效。 这些策略将在下一章中详细讨论。例如:若用缩写 rw 表示重写, 那么刚才的证明便可写成如下形式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
variable (a b c d e : Nat)

theorem T : 
    (h1 : a = b)
    (h2 : b = c + 1)
    (h3 : c = d)
    (h4 : e = 1 + d)
    a = e :=
  calc
    a = b      := by rw [h1]
    _ = c + 1  := by rw [h2]
    _ = d + 1  := by rw [h3]
    _ = 1 + d  := by rw [Nat.add_comm]
    _ = e      := by rw [h4]

rw 策略本质上会利用一个给定的等式 (可以是一个假设、一个定理名或者是一个复杂的项)来“重写”目标; 如果这样做能将目标简化为一种等式 t = t, 那么该策略就会应用自反性来证明它。

重写可以应用到一连串等式上, 因此刚才的证明可以简化为如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
variable (a b c d e : Nat)

theorem T
    (h1 : a = b)
    (h2 : b = c + 1)
    (h3 : c = d)
    (h4 : e = 1 + d) :
    a = e :=
  calc
    a = d + 1  := by rw [h1, h2, h3]
    _ = 1 + d  := by rw [Nat.add_comm]
    _ = e      := by rw [h4]

甚至还可以更简洁:

1
2
3
4
5
6
7
8
9
variable (a b c d e : Nat)

theorem T
    (h1 : a = b)
    (h2 : b = c + 1)
    (h3 : c = d)
    (h4 : e = 1 + d) :
    a = e :=
  by rw [h1, h2, h3, Nat.add_comm, h4]

相反,simp 策略可以 以任意顺序 在项中任何适用的地方 重复应用给定的等式 来重写目标。它 还可以使用之前声明给系统的其他规则,且 还可以明智地应用交换性以避免陷入死循环。 如此我们便可如下证明定理:

1
2
3
4
5
6
7
8
9
variable (a b c d e : Nat)

theorem T
    (h1 : a = b)
    (h2 : b = c + 1)
    (h3 : c = d)
    (h4 : e = 1 + d) :
    a = e :=
  by simp [h1, h2, h3, Nat.add_comm, h4]

我们将在下一章讨论 rwsimp 的变体。

calc 命令可以针对任何支持某种形式传递性的关系进行配置。 它甚至可以组合不同的关系。

1
2
3
4
5
6
7
8
variable (a b c d : Nat)

example (h1 : a = b) (h2 : b  c) (h3 : c + 1 < d) : a < d :=
  calc
    a = b     := h1
    _ < b + 1 := Nat.lt_succ_self b
    _  c + 1 := Nat.succ_le_succ h2
    _ < d     := h3

你还可以“教会” calc 新的传递性定理—— 只需加入 Trans 类型类下的新实例即可。 类型类会在后面介绍。 下面的小例子将演示 如何使用新的 Trans 实例 扩展 calc 表示法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def divides (x y : Nat) : Prop :=
   k, k*x = y

def divides_trans (h₁ : divides x y) (h₂ : divides y z) : divides x z :=
  let k₁, d₁ := h₁
  let k₂, d₂ := h₂
  k₁ * k₂, by rw [Nat.mul_comm k₁ k₂, Nat.mul_assoc, d₁, d₂]

def divides_mul (x : Nat) (k : Nat) : divides x (k*x) :=
  k, rfl

instance : Trans divides divides divides where
  trans := divides_trans

example (h₁ : divides x y) (h₂ : y = z) : divides x (2*z) :=
  calc
    divides x y     := h₁
    _ = z           := h₂
    divides _ (2*z) := divides_mul ..

infix:50 " ∣ " => divides

example (h₁ : divides x y) (h₂ : y = z) : divides x (2*z) :=
  calc
    x  y   := h₁
    _ = z    := h₂
    _  2*z := divides_mul ..

上面的例子清楚地表明: 即便关系式中没有中缀符号, 你也依然可以使用 calc。 由于 Lean 已经包含了除法对应的标准 Unicode 符号 (即 ,可以通过输入 \dvd\mid 来打出它), 上述例子采用的是普通的竖线以避免冲突; 但在实际使用中这可不是个好主意, 因为它可能会和 match ... with 表达式中的 ASCII | 混淆。

有了 calc 我们就可以以一种 更自然、更清晰的方式写出上一节的证明。

1
2
3
4
5
6
7
8
variable (x y : Nat)

example : (x + y) * (x + y) = x * x + y * x + x * y + y * y :=
  calc
    (x + y) * (x + y) = (x + y) * x + (x + y) * y  := by rw [Nat.mul_add]
    _ = x * x + y * x + (x + y) * y                := by rw [Nat.add_mul]
    _ = x * x + y * x + (x * y + y * y)            := by rw [Nat.add_mul]
    _ = x * x + y * x + x * y + y * y              := by rw [Nat.add_assoc]

这里值得考虑另一种 calc 表示法。当第一个表达式占用这么多空间时, 在第一个关系中使用 _ 自然会对齐所有关系式:

还有一种 calc 记法值得考虑: 当第一个表达式占用很多空间时, 在第一对关系中使用 _ 便可以自然地对齐所有关系式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
variable (x y : Nat)

example : (x + y) * (x + y) = x * x + y * x + x * y + y * y
  :=calc (x + y) * (x + y)
    _ =  (x + y) * x + (x + y) * y         :=        Nat.mul_add (x + y) x y -- 获得 h1
    _ =  (x * x + y * x) + (x + y) * y     := by rw [Nat.add_mul x y x]
    _ =  (x * x + y * x) + (x * y + y * y) := by rw [Nat.add_mul x y y] -- 获得 h2
--              a             b       c
    _ = ((x * x + y * x) +  x * y)+ y * y  := by rw[Nat.add_assoc] -- 相当于加了一个 Eq.symm
--  _ = ((x * x + y * x) +  x * y)+ y * y  := by rw [Nat.add_assoc (x * x + y * x) (x * y) (y * y)]
注:演示一下 rw[←Nat.add_assoc] 存在与否的区别
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
variable (x y : Nat)

example : (x + y) * (x + y) = x * x + (y * x + (x * y + y * y))
  :=calc (x + y) * (x + y)
    _ =  (x + y) * x + (x + y) * y         :=        Nat.mul_add (x + y) x y -- 获得 h1
    _ =  (x * x + y * x) + (x + y) * y     := by rw [Nat.add_mul x y x]
    _ =  (x * x + y * x) + (x * y + y * y) := by rw [Nat.add_mul x y y] -- 获得 h2
--          a       b             c
    _ =   x * x + (y * x + (x * y + y * y)):= by rw [Nat.add_assoc]

-- 这里是 Nat.add_assoc 的定义
-- Nat.add_assoc :
-- (a + b) + c = a + (b + c)

这里 Nat.add_assoc 之前的左箭头(可以输入 \l 或者是对应的 ASCII 码 <-) 指示 rewrite以相反的方向应用该等式。如果追求简洁, 那么 rwsimp 各自都可以单独完成这项任务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
variable (x y : Nat)

example : (x + y) * (x + y) = x * x + y * x + x * y + y * y := by
  rw [Nat.mul_add, 
      Nat.add_mul, 
      Nat.add_mul, 
      Nat.add_assoc]

example : (x + y) * (x + y) = x * x + y * x + x * y + y * y := by
  simp [Nat.mul_add, 
        Nat.add_mul, 
        Nat.add_assoc]

下面是一些与等式有关的练习:


记住:在没有任何参数的情况下,Prop 类型的表达式只是一个断言。请 填写下面 primeFermat_prime 的定义并 构造每个给定的断言。举个例子:你可以通过声称 对每个自然数 n 都存在一个大于 n 的素数来表示素数有无穷多个。 哥德巴赫弱猜想 Goldbach’s weak conjecture声称 每个大于 5 的奇数都可以表示为三个素数之和。如果有必要, 请查阅费马素数 Fermat prime 的定义或任何其他陈述。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def even (n : Nat) : Prop := sorry

def prime (n : Nat) : Prop := sorry

def infinitely_many_primes : Prop := sorry

def Fermat_prime (n : Nat) : Prop := sorry

def infinitely_many_Fermat_primes : Prop := sorry

def goldbach_conjecture : Prop := sorry

def Goldbach's_weak_conjecture : Prop := sorry

def Fermat's_last_theorem : Prop := sorry
  • 答:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    def divides (n m : Nat) : Prop :=
       (k : Nat), k * n = m
    
    def even (n : Nat) : Prop :=
      divides 2 n
    
    def prime (n : Nat) : Prop :=
       (k : Nat), k != n  k != n  ¬(divides k n)
    
    def infinitely_many_primes : Prop :=
       (n : Nat),  (k : Nat), k > n  prime k
    
    def Fermat_prime (n : Nat) : Prop :=
      prime n   (k : Nat), (2^k + 1= n)
    
    def infinitely_many_Fermat_primes : Prop :=
       (n : Nat),  (k : Nat), k > n  Fermat_prime k
    
    def goldbach_conjecture : Prop :=
       (n : Nat), n > 2  even n   (p q : Nat), prime p  prime q  p + q = n
    
    def Goldbach's_weak_conjecture : Prop :=
       (n : Nat), n > 2  even n 
         (p q r : Nat), prime p  prime q  prime r 
          p + q + r = n
    
    def Fermat's_last_theorem : Prop :=
       (n : Nat), n > 2 
        ¬∃ (a b c : Nat), a^n + b^n = c^n

4.4. 存在量词 The Existential Quantifier

最后我们来看看存在量词:其可以写成 exists x : α, p x,也可以写成 ∃ x : α, p x。 这两种写法其实都是一个更冗长的表达式的缩写,即 Exists (fun x : α => p x),定义位于 Lean 库中。

如你所想的那样,Lean 库中包含了 一个引入规则和 一个消去规则。 引入规则很直白: 要想证明 ∃ x : α, p x, 只需提供一个合适的项 t 以及 p t 的证明即可。 下面是一些例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
example :  x : Nat, x > 0 :=
  have h : 1 > 0 := Nat.zero_lt_succ 0
  Exists.intro 1 h -- 1 对应 α 下的项 w ,h 对应 p w 下的证明

example (x : Nat) (h : x > 0) :  y, y < x :=
  Exists.intro 0 h

example (x y z : Nat) (hxy : x < y) (hyz : y < z) :  w, x < w  w < z :=
  Exists.intro y (And.intro hxy hyz)

#check @Exists.intro
-- @Exists.intro : ∀ {α : Sort u_1} {p : α → Prop} (w : α), p w → Exists p
#check Exists.intro
-- Exists.intro.{u} {α : Sort u} {p : α → Prop} (w : α) (h : p w) : Exists p

我们可以用匿名构造子表示法 ⟨t, h⟩ 代替 Exists.intro t h, 如果类型可从语境中推断出的话:

1
2
3
4
5
6
7
8
9
example :  x : Nat, x > 0 :=
  have h : 1 > 0 := Nat.zero_lt_succ 0
  1, h -- 1 对应 α 下的项 w ,h 对应 p w 下的证明

example (x : Nat) (h : x > 0) :  y, y < x :=
  0, h

example (x y z : Nat) (hxy : x < y) (hyz : y < z) :  w, x < w  w < z :=
  y, hxy, hyz

注意到 Exists.intro 有隐式参数: Lean 必须在结论 ∃ x, p x 中推断出谓词 p : α → Prop,而这并非易事;例如, 如果我们有 hg : g 0 0 = 0 并且写下 Exists.intro 0 hg, 那么谓词 p 就会有许多可能的取值,比如分别对应于命题 ∃ x, g x x = x∃ x, g x x = 0∃ x, g x 0 = x,等等。 Lean 会利用语境来推断出哪个是合适的, 下面的例子对此进行了说明:当中 我们将选项 pp.explicit 设置为 true 以要求 Lean 美观显示隐式参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
variable (g : Nat  Nat  Nat)

theorem gex1 (hg : g 0 0 = 0) :  x, g x x = x := 0, hg
theorem gex2 (hg : g 0 0 = 0) :  x, g x 0 = x := 0, hg
theorem gex3 (hg : g 0 0 = 0) :  x, g 0 0 = x := 0, hg
theorem gex4 (hg : g 0 0 = 0) :  x, g x x = 0 := 0, hg

set_option pp.explicit true  -- 打印隐式参数

#print gex1
-- theorem gex1 : ∀ (g : Nat → Nat → Nat),
--   @Eq Nat
--       (g (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))
--         (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))))
--       (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) →
--     @Exists Nat fun x => @Eq Nat (g x x) x :=
-- fun g hg => @Exists.intro Nat (fun x => @Eq Nat (g x x) x) (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) hg
#print gex2
-- theorem gex2 : ∀ (g : Nat → Nat → Nat),
--   @Eq Nat
--       (g (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))
--         (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))))
--       (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) →
--     @Exists Nat fun x => @Eq Nat (g x (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))) x :=
-- fun g hg =>
--   @Exists.intro Nat (fun x => @Eq Nat (g x (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))) x)
--     (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) hg
#print gex3
-- theorem gex3 : ∀ (g : Nat → Nat → Nat),
--   @Eq Nat
--       (g (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))
--         (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))))
--       (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) →
--     @Exists Nat fun x =>
--       @Eq Nat
--         (g (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))
--           (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))))
--         x :=
-- fun g hg =>
--   @Exists.intro Nat
--     (fun x =>
--       @Eq Nat
--         (g (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))
--           (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))))
--         x)
--     (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) hg
#print gex4
-- theorem gex4 : ∀ (g : Nat → Nat → Nat),
--   @Eq Nat
--       (g (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0)))
--         (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))))
--       (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) →
--     @Exists Nat fun x => @Eq Nat (g x x) (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) :=
-- fun g hg =>
--   @Exists.intro Nat (fun x => @Eq Nat (g x x) (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))))
--     (@OfNat.ofNat Nat (nat_lit 0) (instOfNatNat (nat_lit 0))) hg

我们可以将 Exists.intro 视为某种信息隐藏操作, 因为它将断言的具体实例隐藏起来并用存在量词代替。 存在消去规则 Exists.elim 则执行相反的操作, 它允许我们从 ∃ x : α, p x 证明一个命题 q; 这是通过展示对任意 w p w 都能推出 q 来实现的。粗略地说, 既然知道存在一个 x 满足 p x, 那么我们可以给它起个名字,比如 w。 如果 q 没有提到 w, 那么展示“p w 能推出 q”就等同 于展示“q 可以从任何这样的 x 的存在性而推得”。 下面是一个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
variable (α : Type) (p q : α  Prop)

example (h :  x, p x  q x) :  x, q x  p x :=
  Exists.elim h                                       -- 存在消去
    (fun w                                           -- 全称引入
     fun hw : p w  q w                               -- 箭头引入
     show  x, q x  p x from w, hw.right, hw.left)   -- 存在引入 合取引入

#print Exists.elim
-- theorem Exists.elim.{u} :
-- ∀ {α : Sort u}
--   {p : α → Prop}
--   {b : Prop},
-- (∃ x, p x) → (∀ (a : α), p a → b) → b :=
-- fun {α} {p} {b} h₁ h₂ ↦
--  match h₁ with
--  | Exists.intro a h => h₂ a h

将存在消去规则和析取消去规则进行比较可能会带来一些帮助。 命题 ∃ x : α, p x 可以看成一个关于命题 p a 的大型析取, 其中 a 遍历所有 α 中的元素。注意到 匿名构造子 ⟨w, hw.right, hw.left⟩ 是 嵌套的构造子 ⟨w, ⟨hw.right, hw.left⟩⟩ 的缩写。

带存在量词的命题与 Sigma 类型 很相似,后者在依值类型一节中提到过。 区别在于前者是命题,后者是类型。 不然的话它们非常相似。 给定谓词 p : α → Prop 和一族类型 β : α → Type, 对于项 a : αh : p ah' : β a 而言 项 Exists.intro a h 的类型是 (∃ x : α, p x) : Prop,而 项 Sigma.mk a h' 的类型是 (Σ x : α, β x) : TypeΣ 之间的相似性是 Curry-Howard 同构的另一例子。

Lean 提供一个更加方便的消去存在量词的途径, 那就是通过 match 表达式:

1
2
3
4
5
variable (α : Type) (p q : α  Prop)

example (h :  x, p x  q x) :  x, q x  p x
  :=match h with
    | w, hw => w, hw.right, hw.left

match 表达式是 Lean 函数定义系统的一部分, 其提供了方便且富有表达力的方式来定义复杂函数。 Curry-Howard 同构又一次让我们能够采用这种机制来编写证明。 match 语句会将带有存在量词的命题“解构”到组件 whw 中, 后者可以在语句体中被引用以用于证明命题。 我们还可以对 match 添加类型注释以使代码变得更清晰:

1
2
3
4
5
variable (α : Type) (p q : α  Prop)

example (h :  x, p x  q x) :  x, q x  p x :=
  match h with
  | (w : α), (hw : p w  q w) => w, hw.right, hw.left

甚至还可以在 match 语句中把合取也顺便解构掉:

1
2
3
4
5
variable (α : Type) (p q : α  Prop)

example (h :  x, p x  q x) :  x, q x  p x :=
  match h with
  | w, hpw, hqw => w, hqw, hpw

Lean 还提供了一个支持模式匹配的 let 表达式:

1
2
3
4
5
variable (α : Type) (p q : α  Prop)

example (h :  x, p x  q x) :  x, q x  p x :=
  let w, hpw, hqw := h
  w, hqw, hpw

这实际上是上面的 match 结构的替代记法。 Lean 还允许我们在 fun 表达式中使用隐式的 match

1
2
3
4
variable (α : Type) (p q : α  Prop)

example : ( x, p x  q x)   x, q x  p x :=
  fun w, hpw, hqw => w, hqw, hpw

我们将在归纳和递归一章看到: 所有这些变体其实都是更一般的模式匹配构造的实例。

在下面的例子中,我们将 is_even a 定义为 ∃ a', a = 2 * a', 然后证明两个偶数的和是偶数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
variable (a b : Nat)

def is_even (a : Nat) :=  a', a = 2 * a'

theorem even_plus_even (ha : is_even a) (hb : is_even b) : is_even (a + b)
  :=Exists.elim
      (show  a', a = 2 * a' from ha)
      (show  a', (a = 2 * a')  ( c', a + b = 2 * c') from fun
        (a' : Nat) =>
        (show (a = 2 * a')  ( c', a + b = 2 * c') from fun
          (hwa : a = 2 * a') =>
          (show  c', a + b = 2 * c' from Exists.elim
            (show  b', b = 2 * b' from hb)
            (show  b', (b = 2 * b')  ( c', a + b = 2 * c') from fun
              (b' : Nat) =>
              show (b = 2 * b')  ( c', a + b = 2 * c') from fun
                (hwb : b = 2 * b') =>
                (show  c', a + b = 2 * c' from Exists.intro
                  (a' + b' : Nat)
                  (show a + b = 2 * (a' + b') from calc
                        a + b = 2 * a' + 2 * b' := by rw [hwa, hwb]
                            _ = 2 * (a' + b')   := by rw [Nat.mul_add]))))))

使用本章提到的各种小工具—— match 语句、 匿名构造子和 rewrite 策略, 我们可以简洁地写出如下证明:

1
2
3
4
5
6
7
variable (a b : Nat)

def is_even (a : Nat) :=  a', a = 2 * a'

theorem even_plus_even (h1 : is_even a) (h2 : is_even b) : is_even (a + b)
  :=match h1, h2 with
      | a', hwa, b', hwb => a' + b', by rw [hwa, hwb, Nat.mul_add]

正如 构造主义的“或”比古典的“或”强, 构造主义的“存在”也会比古典的“存在”强。 例如下面的推论就需要经典推理: 因为从构造主义的角度来看, 知道“并不是每一个 x 都满足 ¬p”并不等同于 知道“有一个特定的 x 能满足 p”。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
open Classical
variable (p : α  Prop)

example (h : ¬  x, ¬ p x) :  x, p x
  :=(show  x, p x from byContradiction
      (show ¬( x, p x)  False from fun
        (h1 : ¬  x, p x) =>
        have h2 :=
          (show  x, ¬p x from fun
            (x) =>
            (show p x  False from fun
              (h3 : p x) =>
              have h4 :=
                (show  x, p x from x, h3)
              (show False from h1 h4)))
        show False from h h2))

下面是一些涉及到存在量词的常见等式。在下面的练习中, 我们鼓励你写出尽可能多的证明。 至于当中哪些是非构造主义的,需要你自己判断。 因此需要用到一些经典逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
open Classical

variable (α : Type) (p q : α  Prop)
variable (r : Prop)

example : ( x : α, r)  r := sorry
example (a : α) : r  ( x : α, r) := sorry
example : ( x, p x  r)  ( x, p x)  r := sorry
example : ( x, p x  q x)  ( x, p x)  ( x, q x) := sorry

example : ( x, p x)  ¬ ( x, ¬ p x) := sorry
example : ( x, p x)  ¬ ( x, ¬ p x) := sorry
example : (¬  x, p x)  ( x, ¬ p x) := sorry
example : (¬  x, p x)  ( x, ¬ p x) := sorry

example : ( x, p x  r)  ( x, p x)  r := sorry
example (a : α) : ( x, p x  r)  ( x, p x)  r := sorry
example (a : α) : ( x, r  p x)  (r   x, p x) := sorry

注意到 第二个例子和 最后两个例子 要求假设至少有一个类型为 α 的元素 a

以下是两个比较困难的问题的解:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
open Classical

variable (α : Type) (p q : α  Prop)
variable (a : α)
variable (r : Prop)

example : ( x, p x  q x)  ( x, p x)  ( x, q x) :=
  Iff.intro
    (fun a, (h1 : p a  q a) =>
      Or.elim h1
        (fun hpa : p a => Or.inl a, hpa)
        (fun hqa : q a => Or.inr a, hqa))
    (fun h : ( x, p x)  ( x, q x) =>
      Or.elim h
        (fun a, hpa => a, (Or.inl hpa))
        (fun a, hqa => a, (Or.inr hqa)))

example : ( x, p x  r)  ( x, p x)  r :=
  Iff.intro
    (fun b, (hb : p b  r) =>
     fun h2 :  x, p x =>
     show r from hb (h2 b))
    (fun h1 : ( x, p x)  r =>
     show  x, p x  r from
       byCases
         (fun hap :  x, p x => a, λ h' => h1 hap)
         (fun hnap : ¬  x, p x =>
          byContradiction
            (fun hnex : ¬  x, p x  r =>
              have hap :  x, p x :=
                fun x =>
                byContradiction
                  (fun hnp : ¬ p x =>
                    have hex :  x, p x  r := x, (fun hp => absurd hp hnp)
                    show False from hnex hex)
              show False from hnap hap)))
  1. (∃ x : α, r) → r

    答:

    简单版:

    1
    2
    3
    4
    5
    6
    7
    8
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : ( _ : α, r)  r
      : (h :  _ : α, r)  match h with
        | Exists.intro _ hr => hr

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
    
    example : ( _ : α, r)  r
      :=show ( _ : α, r)  r from fun    -- → 引入
          (h :  _ : α, r) =>              --
          (show r from Exists.elim         -- ∃ 消去
            (show  _ : α, r from h)        --
            (show  _ : α, r  r from fun   -- ∀ 引入
              (_ : α) =>                     --
              (show r  r from fun           -- → 引入
                (hr : r) =>                   --
                (show r from hr))))           --
  2. α → r → (∃ x : α, r)

    答:

    简单版:

    1
    2
    3
    4
    5
    6
    7
    8
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example (a : α) : r  ( _ : α, r)
      : (hr : r) 
        a, hr

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
    
    example (a : α) : r  ( _ : α, r)
      :=show r  ( _ : α, r) from fun       -- → 引入
          (hr : r) =>                         --
          (show  _ : α, r from Exists.intro  -- ∃ 引入
            (a : α)                    --
            (show r from hr))                  --
  3. (∃ x, p x ∧ r) ↔ (∃ x, p x) ∧ r

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : ( x, p x  r)  ( x, p x)  r
      :=Iff.intro
        (λ (h :  x, p x  r)  match h with
          | w, hw => ⟨⟨w, hw.left, hw.right)
        (λ (h : ( x, p x)  r)  match h with
          | ⟨⟨w, hw, hr => w, hw, hr)

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
    
    example : ( x, p x  r)  ( x, p x)  r
      :=Iff.intro
        (show ( x, p x  r)  ( x, p x)  r from fun        -- → 引入
          (h :  x, p x  r) =>                                --
          (show ( x, p x)  r from Exists.elim                -- ∃ 消去
            (show  x, p x  r from h)                          --
            (show  x, p x  r  ( x, p x)  r from fun        -- ∀ 引入
              (x : α) =>                                         --
              (show p x  r  ( x, p x)  r from fun            -- → 引入
                (hw : p x  r) =>                                 --
                (show ( x, p x)  r from And.intro               -- ∧ 引入
                  (show  x, p x from Exists.intro                 -- ∃ 引入
                    (x : α)                                 --
                    (show p x from And.left                         -- ∧ 消去
                      (show p x  r from hw)))                       --
                  (show r from And.right                            -- ∧ 消去
                    (show p x  r from hw)))))))                     --
        (show ( x, p x)  r  ( x, p x  r) from fun        -- → 引入
          (h : ( x, p x)  r) =>                              --
          (show  x, p x  r from Exists.elim                  -- ∃ 消去
            (show  x, p x from And.left                        -- ∧ 消去
              (show ( x, p x)  r from h))                      --
            (show  x, p x   x, p x  r from fun              -- ∀ 引入
              (x : α) =>                                         --
              (show p x   x, p x  r from fun                  -- → 引入
                (hw : p x) =>                                     --
                (show  x, p x  r from Exists.intro              -- ∃ 引入
                  (x : α)                                  --
                  (show p x  r from And.intro                     -- ∧ 引入
                    (show p x from hw)                              --
                    (show r from And.right                          -- ∧ 消去
                      (show ( x, p x)  r from h))))))))            --
  4. (∃ x, p x ∨ q x) ↔ (∃ x, p x) ∨ (∃ x, q x)

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : ( x, p x  q x)  ( x, p x)  ( x, q x)
      :=Iff.intro
        (λ (h :  x, p x  q x)  match h with
        | x, Or.inl hp => Or.inl x, hp
        | x, Or.inr hq => Or.inr x, hq)
        (λ (h : ( x, p x)  ( x, q x))  match h with
        | Or.inl x, hp => x, Or.inl hp
        | Or.inr x, hq => x, Or.inr hq)

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
    
    example : ( x, p x  q x)  ( x, p x)  ( x, q x)
      :=Iff.intro
        (show ( x, p x  q x)  ( x, p x)  ( x, q x) from fun   -- → 引入
          (h :  x, p x  q x) =>                                    --
          show ( x, p x)  ( x, q x) from Exists.elim              -- ∃ 消去
            (show  x, p x  q x from h)                              --
            (show  x, p x  q x  ( x, p x)  ( x, q x) from fun   -- ∀ 引入
              (x : α) =>                                               --
              (show p x  q x  ( x, p x)  ( x, q x) from fun       -- → 引入
                (hpq : p x  q x) =>                                    --
                (show ( x, p x)  ( x, q x) from Or.elim              -- ∨ 消去
                  (show p x  q x from hpq)                              --
                  (show p x  ( x, p x)  ( x, q x) from fun           -- → 引入
                    (hp : p x) =>                                         --
                    show ( x, p x)  ( x, q x) from Or.inl              -- ∨ 引入
                      (show  x, p x from x, hp))                        --
                  (show q x  ( x, p x)  ( x, q x) from fun           -- → 引入
                    (hq : q x) =>                                         --
                    show ( x, p x)  ( x, q x) from Or.inr              -- ∨ 引入
                      (show  x, q x from x, hq))))))                    --
        (show ( x, p x)  ( x, q x)  ( x, p x  q x) from fun   -- → 引入
          (h : ( x, p x)  ( x, q x)) =>                           --
          (show ( x, p x  q x) from Or.elim                        -- ∨ 消去
            (show ( x, p x)  ( x, q x) from h)                     --
            (show ( x, p x)  ( x, p x  q x) from fun              -- → 引入
              (hp :  x, p x) =>                                       --
              (show  x, p x  q x from Exists.elim                     -- ∃ 消去
                (show  x, p x from hp)                                  --
                (show  x, p x  ( x, p x  q x) from fun               -- ∀ 引入
                  (x : α) =>                                              --
                  (show p x  ( x, p x  q x) from fun                   -- → 引入
                    (hp : p x) =>                                          --
                    (show  x, p x  q x from Exists.intro                 -- ∃ 引入
                      (x : α)                                       --
                      (show p x  q x from Or.inl                           -- ∨ 引入
                        (show p x from hp)))))))
            (show ( x, q x)  ( x, p x  q x) from fun              -- → 引入
              (hq :  x, q x) =>                                       --
              (show  x, p x  q x from Exists.elim                     -- ∃ 消去
                (show  x, q x from hq)                                  --
                (show  x, q x  ( x, p x  q x) from fun               -- ∀ 引入
                  (x : α) =>                                              --
                  (show q x  ( x, p x  q x) from fun                   -- → 引入
                    (hq : q x) =>                                          --
                    (show  x, p x  q x from Exists.intro                 -- ∃ 引入
                      (x : α)                                       --
                      (show p x  q x from Or.inr                           -- ∨ 引入
                        (show q x from hq)))))))))                          --
  5. (∀ x, p x) ↔ ¬(∃ x, ¬p x)

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    open Classical
    
    example : ( x, p x)  ¬( x, ¬p x)
      := Iff.intro
         (λ (hall :  x, p x) 
          λ (hext :  x, ¬p x)  match hext with
           | x, hn => hn (hall x))
         (λ (h : ¬ ( x, ¬p x)) 
          λ (a : α)  byContradiction
          λ (hn : ¬p a)  h a, hn)
    
    -- 这里附上 (p ∧ q) ↔ ¬(¬p ∨ ¬q) 的证明作为对比
    
    variable
    (p q : Prop)
    
    example : (p  q)  ¬(¬p  ¬q)
      :=Iff.intro
        (λ (hpq : p  q) 
         λ (hnpq : ¬p  ¬q)  match hnpq with
          | Or.inl hnp => hnp hpq.left
          | Or.inr hnq => hnq hpq.right)
        (λ (hnnpq : ¬(¬p  ¬q))  And.intro
         (byContradiction (λ (hnp : ¬p)  hnnpq (Or.inl hnp)))
         (byContradiction (λ (hnq : ¬q)  hnnpq (Or.inr hnq))))

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
    
    open Classical
    
    example : ( x, p x)  ¬( x, ¬p x)
      :=Iff.intro                                    -- ↔ 引入
        (show ( x, p x)  ¬( x, ¬p x) from fun      -- → 引入
          (hall :  x,  p x) =>                       --
          (show ¬( x, ¬p x) from fun                 -- ¬ 引入
            (hext :  x, ¬p x) =>                      --
            (show False from Exists.elim               -- ∃ 消去
              (show  x, ¬p x from hext)                --
              (show  x, ¬p x  False from fun          -- ∀ 引入
                (x : α) =>                               --
                (show ¬p x  False from fun              -- → 引入
                  (hn : ¬p x) =>                          --
                  (show False from To.elim                -- ¬ 消去
                    (show ¬p x from hn)                    --
                    (show  p x from All.elim               -- ∀ 消去
                      (show  x, p x from hall)             --
                      (x : α))))))))                --
        (show ¬( x, ¬p x)  ( x, p x) from fun      -- → 引入
          (hextn : ¬( x, ¬p x)) =>                    --
          (show  x, p x from fun                      -- ∀ 引入
            (x : α) =>                                  --
            (show p x from To.elim                      -- → 消去
              (show ¬¬p x  p x from dne)                --
              (show ¬p x  False from fun                -- → 引入
                (hn : ¬p x) =>                            --
                (show False from To.elim                  -- ¬ 消去
                  (show ¬( x, ¬p x) from hextn)           --
                  (show    x, ¬p x  from Exists.intro     -- ∃ 引入
                    (x : α)                         --
                    (show ¬p x from hn)))))))               --
  6. (∃ x, p x) ↔ ¬(∀ x, ¬p x)

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    open Classical
    
    example : ( x, p x)  ¬( x, ¬p x)
      :=Iff.intro
        (λ (hext :  x,  p x) 
         λ (hall :  x, ¬p x)  match hext with
          | x, hp => (hall x) hp)
        (λ (halln : ¬( x, ¬p x))  byContradiction
         λ (hextn : ¬( x,  p x))  halln
          (λ (a : α) 
           λ (hp : p a)  hextn a, hp))
    
    -- 这里附上 (p ∨ q) ↔ ¬(¬p ∧ ¬q) 的证明作为对比
    
    variable
    (p q : Prop)
    
    example : (p  q)  ¬(¬p  ¬q)
      :=Iff.intro
        (λ (hpq : p  q) 
         λ (hnpnq : ¬p  ¬q)  match hpq with
          | Or.inl hp => (hnpnq.left) hp
          | Or.inr hq => (hnpnq.right) hq)
        (λ (hnnpnq : ¬(¬p  ¬q))  byContradiction
         λ (hnpq : ¬(p  q))  hnnpnq (And.intro
          (λ (hp : p)  hnpq (Or.inl hp))
          (λ (hq : q)  hnpq (Or.inr hq))))

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
    
    open Classical
    
    theorem dne {p : Prop} : ¬¬p  p
      : (hnnp : ¬¬p)  byCases
          (λ (hp : p)  hp)
          (λ (hnp : ¬p)  absurd hnp hnnp)
    
    example : ( x, p x)  ¬( x, ¬p x)
      :=Iff.intro                                -- ↔ 引入
        (show ( x, p x)  ¬( x, ¬p x) from fun  -- → 引入
          (hext :  x,  p x) =>                    --
          (show ( x, ¬p x)  False from fun       -- ¬ 引入
            (hall :  x, ¬p x) =>                   --
            (show False from Exists.elim            -- ∃ 消去
              (show  x, p x from hext)              --
              (show  x, p x  False from fun        -- ∀ 引入
                (x : α) =>                            --
                (show p x  False from fun            -- → 引入
                  (hp : p x) =>                        --
                  (show False from To.elim             -- → 消去
                    (show ¬p x from All.elim            -- ∀ 消去
                      (show  x, ¬p x from hall)         --
                      (x : α))                   --
                    (show  p x from hp)))))))           --
        (show ¬( x, ¬p x)  ( x, p x) from fun  -- → 引入
          (halln : ¬( x, ¬p x)) =>                --
          (show  x, p x from To.elim              -- → 消去
            (show ¬¬( x, p x)   x, p x from dne) --
            (show ¬( x, p x)  False from fun      -- ¬ 引入
              (hextn : ¬( x,  p x)) =>              --
              (show False from To.elim               -- ¬ 消去
                (show ¬( x, ¬p x) from halln)        --
                (show  x, ¬p x from fun              -- ∀ 引入
                  (x : α) =>                           --
                  (show p x  False from fun           -- ¬ 引入
                    (hp : p x) =>                       --
                    (show False from To.elim            -- ¬ 消去
                      (show ¬( x, p x) from hextn)      --
                      (show    x, p x  from Exists.intro-- ∃ 引入
                        (x : α)                   --
                        (show p x from hp)))))))))        --
  7. ¬(∃ x, p x) ↔ (∀ x, ¬p x)

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    example : ¬( x, p x)  ( x, ¬p x)
      :=Iff.intro
        (λ (hextn : ¬∃ x, p x) 
         λ (x : α) 
         λ (hp : p x) 
         hextn x, hp)
        (λ (halln :  x, ¬p x) 
         λ (hext :  x, p x)  
         Exists.elim hext halln)
    
    -- 这里附上 ¬(p ∨ q) ↔ ¬p ∧ ¬q 的证明作为对比(见上一章练习第 9 题)
    
    variable
    (p q : Prop)
    
    example : ¬(p  q)  ¬p  ¬q
      :=Iff.intro
        (λ (hnpq : ¬(p  q)) 
         (λ (hp : p)  hnpq (Or.inl hp)),
          (λ (hq : q)  hnpq (Or.inr hq)))
        (λ (hnpnq : ¬p  ¬q) 
         λ (hpq : p  q)  match hpq with -- 对应 Or.elim
           | Or.inl hp => (hnpnq.left)  hp
           | Or.inr hq => (hnpnq.right) hq)

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
    
    example : (¬∃ x, p x)  ( x, ¬p x)
      :=Iff.intro
        (show (¬∃ x, p x)  ( x, ¬p x) from fun     -- → 引入
          (hextn : ¬∃ x, p x) =>                      --
          (show  x, ¬p x from fun                    -- ∀ 引入
            (x : α) =>                                 --
            (show p x  False from fun                 -- ¬ 引入
              (hp : p x) =>                             --
              show False from To.elim                   -- ¬ 消去
                (show ¬∃ x, p x from hextn)              --
                (show   x, p x from Exists.intro        -- ∃ 引入
                  (x : α)                         --
                  (show p x from hp)))))                  --
        (show ( x, ¬p x)  (¬∃ x, p x) from fun     -- → 引入
          (hall :  x, ¬p x) =>                       --
          (show ¬∃ x, p x from fun                    -- ¬ 引入
            (hext :  x,  p x) =>                      --
            (show False from Exists.elim               -- ∃ 消去
              (show  x, p x from hext)                 --
              (show  x, p x  False from hall))))      --
  8. ¬(∀ x, p x) ↔ (∃ x, ¬p x)

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    open Classical
    
    example : (¬∀ x, p x)  ( x, ¬p x)
      :=Iff.intro
        (λ (halln : ¬( x, p x))  byContradiction
          (λ (hextn : ¬( x, ¬p x))  halln
            (λ (a : α)  byContradiction
              (λ (hnp : ¬p a)  hextn a, hnp))))
        (λ (hext :  x, ¬p x) 
         λ (hall :  x,  p x)  match hext with
          | w, hnpw => hnpw (hall w))
    
    -- 这里附上 ¬(p ∧ q) ↔ (¬p ∨ ¬q) 的证明作为对比(见上一章练习第 20 题和第 10 题)
    
    variable
    (p q : Prop)
    
    example : ¬(p  q)  (¬p  ¬q)
      :=Iff.intro
        (λ (hnpq : ¬(p  q))  byCases
          (λ (hp : p)  Or.inr
            (λ (hq : q)  hnpq hp, hq))
          (λ (hnp : ¬p)  Or.inl hnp))
        (λ (hnpnq : ¬p  ¬q) 
         λ (hpq : p  q)  match hnpnq with -- 对应 Or.elim
          | Or.inl hnp  => hnp (And.left  hpq)
          | Or.inr hnq  => hnq (And.right hpq))

    复杂版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    def To.elim {p q : Prop} : (p  q)  p  q
      : (hpq : p  q) (hp : p)  hpq hp
    
    def All.elim {α : Type} {p : α  Prop} : ( x : α, p x)  (a : α)  p a
    -- (a : α) → p a 相当于 ∀ (a : α), p a
      : (h :  x : α, p x) (a : α)  h a
       
    open Classical
    
    theorem dne {p : Prop} : ¬¬p  p
      : (hnnp : ¬¬p)  byCases
          (λ (hp : p)  hp)
          (λ (hnp : ¬p)  absurd hnp hnnp)
          
    example : (¬∀ x, p x)  ( x, ¬p x)
      :=Iff.intro                                    -- ↔ 引入
        (show (¬∀ x, p x)  ( x, ¬p x) from fun      -- → 引入
          (halln : ¬( x, p x)) =>                     --
          (show  x, ¬p x from To.elim                 -- → 消去
            (show ¬¬( x, ¬p x)  ( x, ¬p x) from dne) --
            (show  ¬( x, ¬p x)  False from fun        -- → 消去
              (hextn : ¬( x, ¬p x)) =>                  --
              let hall :=                                  --
                show  x,  p x from fun                     -- ∀ 引入
                  (x : α) =>                                 --
                  (show p x from To.elim                     -- → 消去
                    (show ¬¬p x  p x from dne)               --
                    (show  ¬p x  False from fun              --
                      (hnp : ¬p x) =>                          --
                      (show False from To.elim                 -- → 消去
                        (show ¬( x, ¬p x) from hextn)          --
                        (show  x, ¬p x from Exists.intro       -- ∃ 引入
                          (x : α)                        --
                          (show ¬p x from hnp)))))               --
              (show False from To.elim                   -- → 消去
                (show ¬( x, p x) from halln)             --
                (show    x, p x  from hall)))))          --
        (show ( x, ¬p x)  (¬∀ x, p x) from fun      -- → 引入
          (hext :  x, ¬p x) =>                        --
          (show ( x, p x)  False from fun            -- ¬ 引入
            (hall :  x,  p x) => Exists.elim           -- ∃ 消去
              (show  x, ¬p x from hext)                 --
              (show  x, ¬p x  False from fun           -- ∀ 引入
                (x : α) =>                                --
                (show ¬p x  False from fun               -- → 引入
                  (hnp : ¬p x) =>                          --
                  (show False from To.elim                 -- → 消去
                    (show ¬p x from hnp)                    --
                    (show  p x from All.elim                -- ∀ 消去
                      (show  x, p x from hall)              --
                      (x : α)))))))                  --
  9. (∀ x, p x → r) ↔ (∃ x, p x) → r

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    variable 
    (α : Type) 
    (p q : α  Prop)
    (r : Prop)
    
    example : ( x, p x  r)  (( x, p x)  r)
      :=Iff.intro
        (λ (hallpr :  x, p x  r)  
         λ (hextp  :  x, p x)  match hextp with
          | x, hp => hallpr x hp)
        (λ (hextpr : ( x, p x)  r)  
         λ (x : α) 
         λ (hp : p x) 
         hextpr x , hp)

    复杂版:

    懒得写了。略。

  10. α → ((∃ x, p x → r) ↔ (∀ x, p x) → r)

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    open Classical
    
    example : ( x, p x  r)  ( x, p x)  r
      : (hextpr :  x, p x  r) 
        λ (hallp  :  x, p x)  match hextpr with
        | x, hpr => hpr (hallp x)
    example : α  (( x, p x)  r)  ( x, p x  r)
      : (x : α) 
        λ (hallpr : ( x, p x)  r)  byContradiction
          (λ (hnex : ¬( x, p x  r)) 
            (let hallp :  x, p x
            : (x : α)  byContradiction
                (λ (hnpx : ¬p x) 
                 hnex x, λ (hp : p x)  absurd hp hnpx)
            hnex x, λ (_ : p x)  hallpr hallp))

    复杂版:

    懒得写了。略。

  11. α → ((∃ x, r → p x) ↔ (r → ∃ x, p x))

    答:

    简单版:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    variable
    (α : Type)
    (p q : α  Prop)
    (r : Prop)
    
    open Classical
    
    example : ( x, r  p x)  (r   x, p x)
      :=(λ (h :  x, r  p x) 
         λ (hr : r)  match h with
         | x, hrp => x, hrp hr)
    example (x : α) : (r   x, p x)  ( x, r  p x)
      : (hrextp : r   x, p x)  byCases
        (λ (hr : r)  match (hrextp hr) with
          | x, hp => x, λ (_: r)  hp)
        (λ (hnr : ¬r) 
        x, (λ (hr : r)  absurd hr hnr))

    复杂版:

    懒得写了。略。

4.5. 再来些证明语法 More on the Proof Language

我们已经看到:像 funhaveshow 这样的关键字 可使得形式化的证明项能够反映出非正式数学证明的结构。 本节我们将讨论证明语言的一些附加特性,它们会在实际使用中带来便利。

首先我们可以使用 have 表达式引入匿名的辅助目标。 若是想引用最后一个以此方式引入的表达式, 则可以考虑使用关键字 this

1
2
3
4
5
6
7
variable (f : Nat  Nat)
variable (h :  x : Nat, f x  f (x + 1))

example : f 0  f 3 :=
  have : f 0  f 1 := h 0
  have : f 0  f 2 := Nat.le_trans this (h 1)
  show f 0  f 3 from Nat.le_trans this (h 2)

由于证明通常是从一个事实转移到另一个事实, 因此这可以有效地消除大量杂乱标签。

当目标是可被推断的时,我们也可以让 Lean 自己补充证明——只需写下 by assumption

1
2
3
4
5
6
7
variable (f : Nat  Nat)
variable (h :  x : Nat, f x  f (x + 1))

example : f 0  f 3 :=
  have : f 0  f 1 := h 0
  have : f 0  f 2 := Nat.le_trans (by assumption) (h 1)
  show f 0  f 3 from Nat.le_trans (by assumption) (h 2)

这会让 Lean 使用 assumption 策略—— 即通过在局部语境中找到合适的假设来证明目标。 我们将在下一章中 学到更多关于 assumption 策略的内容。

我们也可以通过写下 ‹p› 来要求 Lean 填写证明: 其中 p 是命题,我们希望 Lean 能在语境中找到它的证明。 你可以分别使用 \f<\f> 来输入这些角引号。 字母“f”表示“French”,因为对应的 unicode 符号也可以作为法语引号。 这个符号在 Lean 中的定义其实是这样的:

1
notation "‹" p "›" => show p by assumption

这种方法比使用 by assumption 更稳健, 因为需要推断的假设类型是显式给出的。 它还使证明更具可读性。这里有一个更详细的例子:

1
2
3
4
5
6
7
8
9
variable (f : Nat  Nat)
variable (h :  x : Nat, f x  f (x + 1))

example : f 0  f 1  f 1  f 2  f 0 = f 2 :=
  fun _ : f 0  f 1 =>
  fun _ : f 1  f 2 =>
  have : f 0  f 2 := Nat.le_trans f 1  f 2 f 0  f 1
  have : f 0  f 2 := Nat.le_trans (h 0) (h 1)
  show f 0 = f 2 from Nat.le_antisymm this f 0  f 2

请留意:法语引号可用来指代语境中的“任何东西”, 而非仅局限于通过匿名方式引入的东西。 它的使用也并非局限于命题—— 尽管将其用于其他数据类型看起来可能会有点怪:

1
example (n : Nat) : Nat := Nat

接下来我们将展示如何使用 Lean 中的宏系统扩展证明语言。

索引 Index

术语

ID 使用 term:<中文术语>

语法

ID 使用 synt:<关键字>

代码块

ID 使用 S<小节>.code<编号>

练习

ID 使用 exer<编号>

第一部分(全称量词与合取、蕴含、析取的关系):

  1. (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x)
  2. (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x)
  3. (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x

第二部分(全称量词的删去):

  1. α → ((∀ x : α, r) ↔ r)
  2. (∀ x, p x ∨ r) ↔ (∀ x, p x) ∨ r
  3. (∀ x, r → p x) ↔ (r → ∀ x, p x)

第三部分(理发师悖论):

第四部分(自然数与等式):

第五部分(存在量词):

  1. (∃ x : α, r) → r
  2. α → r → (∃ x : α, r)
  3. (∃ x, p x ∧ r) ↔ (∃ x, p x) ∧ r
  4. (∃ x, p x ∨ q x) ↔ (∃ x, p x) ∨ (∃ x, q x)
  5. (∀ x, p x) ↔ ¬(∃ x, ¬p x)
  6. (∃ x, p x) ↔ ¬(∀ x, ¬p x)
  7. ¬(∃ x, p x) ↔ (∀ x, ¬p x)
  8. ¬(∀ x, p x) ↔ (∃ x, ¬p x)
  9. (∀ x, p x → r) ↔ (∃ x, p x) → r
  10. α → ((∃ x, p x → r) ↔ (∀ x, p x) → r)
  11. α → ((∃ x, r → p x) ↔ (r → ∃ x, p x))
给我买杯饮料!
Lean-zh 微信微信