Coq/Lean 交互式教程

从零开始学习 Coq 和 Lean 证明助手

第一章:入门基础

1.1 计算机辅助证明:为什么需要 Coq 和 Lean?

我们为什么需要形式化验证?

在关键系统中,软件或硬件的错误可能带来灾难性后果:

  • 1994年奔腾浮点除法错误:Intel 奔腾处理器的浮点运算单元存在 bug,导致某些除法运算结果错误,Intel 损失数亿美元。
  • 1996年阿丽亚娜5号火箭爆炸:由于软件整数溢出错误,价值5亿美元的火箭在发射37秒后自毁。
  • 复杂数学证明的挑战:四色定理、开普勒猜想等现代数学证明极其复杂,人工审查几乎不可能完全验证其正确性。

Coq 和 Lean 就是解决这类问题的工具。它们不仅能发现错误,更能从数学上保证代码或证明的正确性。

什么是交互式定理证明?

想象你是一位老师,指导一个非常聪明但缺乏创造力的学生(Coq/Lean)证明数学定理:

  • 你告诉它每一步该做什么(应用哪个定理、如何化简表达式)
  • 它会忠实地执行并告诉你当前的证明状态
  • 它保证每一步的逻辑都是绝对正确的

这就是"交互式"的含义——人提供创造性的证明思路,机器保证逻辑的严格性。

Coq:久经考验的形式化先驱

Coq 诞生于1984年,由法国国家信息与自动化研究所(INRIA)开发。它的名字来源于其理论基础的提出者 Thierry Coquand。

  • 理论基础:归纳构造演算(Calculus of Inductive Constructions, CIC),这是一种强大的类型论
  • 核心哲学:"命题即类型,证明即程序"——每个数学命题对应一个类型,证明就是构造该类型的一个项
  • 成熟应用:CompCert(完全验证的C编译器)、四色定理机器证明、密码学协议验证等
  • 特色:拥有强大的策略语言 Ltac,支持从证明中提取可执行程序

Lean:现代数学家的新宠

Lean 由微软研究院的 Leonardo de Moura 于2013年发起。Lean 4(2021年发布)是一次重大升级,将证明助手和编程语言完美融合。

  • 设计理念:不仅是证明助手,更是功能完备的编程语言,性能媲美传统编译语言
  • 元编程能力:可以用 Lean 自身编写新的证明策略,扩展性极强
  • 数学社区:拥有 Mathlib,可能是世界上最大的形式化数学库,涵盖本科到研究生级别的数学内容
  • 现代化体验:语法更接近现代编程语言,VS Code 集成体验优秀

核心思想:命题即类型

理解 Coq 和 Lean 的关键是理解"命题即类型"(Curry-Howard 同构):

  • 在普通编程中,intstring 是类型,42"hello" 是这些类型的值
  • 在 Coq/Lean 中,命题(如 2 + 2 = 4)也是一个类型
  • 该命题的证明就是这个类型的一个
  • 如果能构造出这个值,就证明了命题;构造不出,就无法证明

这个革命性的思想将"证明数学定理"转化为"编写类型正确的程序"。

Coq vs Lean:快速对比

特性 Coq Lean
发布时间 1984年 2013年(Lean 4: 2021年)
主要应用领域 软件验证、编译器验证 数学形式化、教学
语法风格 ML/OCaml 风格 类似 Haskell/Scala
IDE 支持 CoqIDE、VS Code、Emacs VS Code(强烈推荐)
社区规模 成熟、学术导向 快速增长、数学家活跃
核心优势 成熟稳定、工业应用多 现代化、性能好、Mathlib

开始交互式证明

在开始之前,让我们先启动 Coq 的交互式环境。打开终端并输入:

$ coqtop
Welcome to Coq 8.18.0

Coq <

现在你可以输入 Coq 命令了。如果使用 CoqIDE 或 VS Code + Coq 插件,可以获得更好的交互体验。

示例:第一个证明对比

让我们证明一个简单的定理:对于任意自然数 n,n + 0 = n。这个例子展示了两种语言的基本证明结构。

Coq 版本
Theorem plus_n_O : forall n : nat, n + 0 = n.
Proof.
  intros n.        (* 引入任意的 n *)
  induction n.     (* 对 n 进行归纳 *)
  - (* n = 0 *)
    reflexivity.   (* 0 + 0 = 0 *)
  - (* n = S n' *)
    simpl.         (* S n' + 0 = S (n' + 0) *)
    rewrite IHn.   (* 使用归纳假设: n' + 0 = n' *)
    reflexivity.   (* S n' = S n' *)
Qed.               (* 证明完成 *)
Lean 版本
theorem plus_n_zero : ∀ n : Nat, n + 0 = n := by
  intro n      -- 引入任意的 n
  simp         -- 简化并完成证明

关键差异

  • Coq 使用 Proof...Qed 包围证明,Lean 使用 := by
  • Coq 的 intros vs Lean 的 intro(注意单复数)
  • Lean 的 simp 更智能,可以自动完成许多 Coq 需要 simpl + reflexivity 的证明
习题 1.1.1

解释"命题即类型"的含义。如果命题 1 + 1 = 2 是一个类型,那么什么是这个类型的值?

习题 1.1.2

比较 Coq 和 Lean 在以下方面的不同:(1) 主要应用领域 (2) 语法风格 (3) 社区特点

习题 1.1.3

为什么说形式化验证能提供"绝对的正确性"?它与传统的软件测试有什么本质区别?

1.2 安装与环境配置

定理证明器的学习和开发是在 IDE 中交互式完成的。用户不是一次性写完整个文件再编译,而是逐步编写并实时查看证明状态。因此,正确配置开发环境至关重要。

为什么需要 IDE?

交互式定理证明的核心特征:

  • 逐步执行:将光标移动到某处,系统执行到该位置并显示当前状态
  • 实时反馈:立即看到当前的证明目标(Proof Goals)和上下文
  • 类型信息:鼠标悬停即可查看任何表达式的类型
  • 错误定位:精确显示哪一行出现了什么错误

没有 IDE,几乎无法进行任何非平凡的证明开发。

第一步:安装 Coq

使用 opam(推荐方法)

opam 是 OCaml 的包管理器,也是管理 Coq 及其库的标准方式:

# 1. 安装 opam
# macOS
brew install opam

# Linux
sudo apt install opam  # 或其他包管理器

# 2. 初始化 opam
opam init
eval $(opam env)

# 3. 安装 Coq
opam install coq
直接安装
# macOS (Homebrew)
brew install coq

# Linux (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install coq

# Windows
# 从 https://github.com/coq/platform/releases 下载 Coq Platform 安装器
# Coq Platform 包含了 Coq 和常用库

第二步:配置 Coq 开发环境

选项 A:VS Code + Coq LSP(推荐)

这是目前最现代化的 Coq 开发环境:

  1. 安装 Visual Studio Code
  2. 在 VS Code 扩展市场搜索 "Coq LSP" 并安装
  3. 重启 VS Code

特点:语法高亮、自动补全、实时错误提示、集成终端

选项 B:CoqIDE

Coq 官方提供的轻量级 IDE,安装 Coq 时通常会一起安装:

# 启动 CoqIDE
coqide

特点:开箱即用、界面简洁、适合初学者

选项 C:Emacs + Proof General

传统但功能强大的选择,适合 Emacs 用户。

第三步:安装 Lean 4

使用 elan(必需)

elan 是 Lean 的版本管理器,类似于 Rust 的 rustup:

# macOS/Linux
curl https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -sSf | sh

# Windows
# 从 https://github.com/leanprover/elan/releases 下载安装器

# 安装后,重启终端或运行
source ~/.profile
配置 VS Code(必需)

重要:Lean 4 的开发体验与 VS Code 深度绑定,这是唯一推荐的开发环境。

  1. 安装 Visual Studio Code
  2. 在扩展市场搜索 "lean4" 并安装(发布者:leanprover)
  3. 重启 VS Code
理解 lean-toolchain

每个 Lean 项目根目录都有一个 lean-toolchain 文件,指定项目使用的 Lean 版本。VS Code 会自动读取并使用正确的版本,解决了版本兼容性问题。

项目管理

Coq 项目:_CoqProject 文件

Coq 使用 _CoqProject 文件管理项目结构:

# 创建项目目录
mkdir my_coq_project
cd my_coq_project

# 创建 _CoqProject 文件
echo "-R . MyProject" > _CoqProject

# 创建源文件
echo "Definition hello := \"world\"." > Hello.v

_CoqProject 文件示例:

-R . MyProject
-arg -w -notation-overridden
theories/Basic.v
theories/Advanced.v
Lean 项目:lake 构建系统

Lean 4 使用 lake 作为项目构建和包管理器:

# 创建新项目
lake new my_lean_project
cd my_lean_project

# 项目结构
# ├── lakefile.lean    # 项目配置文件
# ├── lean-toolchain   # Lean 版本
# ├── MyLeanProject.lean  # 主文件
# └── MyLeanProject/   # 源代码目录

# 构建项目
lake build

添加 Mathlib 依赖(lakefile.lean):

require mathlib from git
  "https://github.com/leanprover-community/mathlib4.git"
习题 1.2.1:验证你的完整环境

让我们创建一个简单的项目来验证整个工具链是否正确配置。

Coq 环境验证
  1. 创建一个新目录和文件:
    mkdir test_coq
    cd test_coq
    echo "-R . TestCoq" > _CoqProject
  2. 创建文件 Test.v
    Definition my_first_definition : nat := 42.
    Check my_first_definition.
    Compute my_first_definition.
  3. 在 VS Code 中打开这个文件
  4. 将光标放在每一行上,观察右侧面板的输出
Lean 环境验证
  1. 创建一个新的 Lean 项目:
    lake new test_lean
    cd test_lean
  2. 编辑 TestLean.lean
    def myFirstDefinition : Nat := 42
    #check myFirstDefinition
    #eval myFirstDefinition
  3. 在 VS Code 中打开项目
  4. 查看 Lean Infoview 面板的输出

常见问题与解决方案

PATH 问题

症状:command not found: coqc/lean/lake

解决方案:

# Coq (使用 opam)
eval $(opam env)
# 将上述命令添加到 ~/.bashrc 或 ~/.zshrc

# Lean
# 确保 ~/.elan/bin 在 PATH 中
echo 'export PATH="$HOME/.elan/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
VS Code 扩展无法启动

解决步骤:

  1. 重启 VS Code
  2. 查看输出面板(View → Output),选择 "Coq LSP" 或 "Lean 4"
  3. 检查是否有错误信息
  4. 确保先安装了 Coq/Lean,再安装 VS Code 扩展
Lean 版本不匹配

症状:打开项目时提示版本错误

解决:这是正常的。lean-toolchain 文件指定了项目需要的版本,elan 会自动下载并使用正确版本。

网络问题

如果在中国大陆,可能需要配置镜像:

# opam 镜像
opam repository add mirrors https://mirrors.tuna.tsinghua.edu.cn/opam

# 对于 Lean,考虑使用代理或等待自动重试

1.3 基本交互与语法

在依赖类型语言中,一切皆有类型:变量有类型,类型本身也有类型。理解这一点是掌握 Coq 和 Lean 的关键。

核心理念:万物皆类型

在 Coq 和 Lean 中:

  • 42 的类型是 nat / Nat
  • nat / Nat 的类型是 Set / Type
  • 命题(如 2 + 2 = 4)也是类型,属于 Prop
  • 这就是"命题即类型"思想的体现

注释与文档

Coq 注释
(* 这是一个单行注释 *)

(* 这是一个
   多行注释 *)

(** 这是一个文档注释,
    可以被 coqdoc 工具提取生成文档 *)

(* ========== 分隔线 ========== *)
(* 常用于在代码中创建视觉分隔 *)
Lean 注释
-- 这是一个单行注释

/- 这是一个
   多行注释 -/

/-- 这是一个文档注释
    可以被提取生成文档 -/

/-! 模块级文档注释
    通常放在文件开头 -/

最佳实践:为所有公开的定义和定理添加文档注释,说明其用途和参数含义。

基本交互命令

命令 1:查看类型 (Check / #check)
Coq
Check nat.           (* nat : Set *)
Check 3.             (* 3 : nat *)
Check list.          (* list : Type -> Type *)
Check @list.         (* @list : Type -> Type (显示所有参数) *)
Check List.map.      (* 显示时可能包含 ?A 等隐式参数 *)
Lean
#check Nat           -- Nat : Type
#check 3             -- 3 : Nat  
#check List          -- List.{u} (α : Type u) : Type u
#check @List.map     -- 显示所有参数,包括隐式的

理解隐式参数:当你看到 @ 符号或 ?A.{u} 等,这表示存在隐式参数。通常这些参数可以被自动推断,无需手动提供。

命令 2:查看定义 (Print / #print)
Coq
Print nat.           (* 显示 nat 的归纳定义 *)
Print Nat.add.       (* 显示加法的定义 *)
Print plus.          (* plus = Nat.add *)

Definition mydef := 42.
Print mydef.         (* mydef = 42 : nat *)
Lean
#print Nat           -- 显示 Nat 的归纳定义
#print Nat.add       -- 显示加法的定义

def mydef := 42
#print mydef         -- def mydef : Nat := 42

用途:理解库函数的实现、调试自己的定义、学习标准库的写法。

命令 3:计算表达式 (Compute / #eval)
Coq
Compute 3 + 4.       (* = 7 : nat *)
Compute negb true.   (* = false : bool *)

(* Compute 会完全展开计算 *)
Definition big := 1000.
Compute big + big.   (* = 2000 : nat *)
Lean
#eval 3 + 4          -- 7
#eval not true       -- false

-- #eval 执行编译后的代码,速度很快
def big := 1000
#eval big + big      -- 2000

注意:对于复杂或无限的对象,Compute 可能很慢或无法显示有意义的结果。

命令 4:搜索定理 (Search / #find)
Coq
(* 按名称搜索 *)
Search "add" "comm".     (* 查找名称包含 add 和 comm 的定理 *)

(* 按模式搜索 *)
Search (_ + _ = _ + _).  (* 查找加法交换律相关定理 *)
Search (0 + _).          (* 查找关于 0 + _ 的定理 *)

(* 在特定模块中搜索 *)
Search nat inside Nat.
Lean
-- 按模式搜索
#find _ + _ = _ + _      -- 查找加法交换律
#find Nat → Nat → Nat    -- 查找类型为 Nat → Nat → Nat 的函数

-- 使用名称搜索(需要导入)
import Std.Data.List.Basic
#check List.length_append -- 找到后可以查看

技巧:不确定定理名称时,先用模式搜索,找到后用 Print/#print 查看详细定义。

习题 1.3.1

查看以下表达式的类型:

Coq 版本
Check (true && false).
Check (3 =? 4).
Check (if true then 3 else 5).
Lean 版本
#check (true && false)
#check (3 == 4)
#check (if true then 3 else 5)
习题 1.3.2

计算以下表达式:

Coq 版本
Compute 2 + 3 * 4.
Compute negb (negb true).
Compute if (3 =? 3) then 100 else 200.
Lean 版本
#eval 2 + 3 * 4
#eval not (not true)
#eval if (3 == 3) then 100 else 200

导入模块

Coq - Require Import
(* 导入列表模块 *)
Require Import List.
Import ListNotations.  (* 导入 [1; 2; 3] 语法 *)

(* 导入多个模块 *)
Require Import Nat Bool List.

(* 导入但不打开命名空间 *)
Require Import Arith.
Check Nat.add.  (* 需要使用全名 *)
Lean - import
-- 导入基本模块
import Std.Data.List.Basic

-- 导入 Mathlib(如果项目包含)
import Mathlib.Data.Nat.Basic
import Mathlib.Tactic

-- 打开命名空间
open List  -- 现在可以直接使用 map 而非 List.map

定义:常量和函数

基本定义
Coq
(* 推荐:始终写明类型签名 *)
Definition my_number : nat := 42.

(* 函数定义 *)
Definition double (n : nat) : nat := n + n.

(* 类型推断(不推荐) *)
Definition bad_double n := n + n.  (* 类型不明确 *)
Lean
-- 推荐:始终写明类型签名
def myNumber : Nat := 42

-- 函数定义
def double (n : Nat) : Nat := n + n

-- 类型推断(不推荐)
def badDouble n := n + n  -- 类型不明确
局部定义 (let ... in)
Coq
Definition complex_calc (x y : nat) : nat :=
  let sum := x + y in
  let prod := x * y in
  sum + prod.

(* 在证明中使用 let *)
Theorem example : 10 + 20 = 30.
Proof.
  let x := 10 in
  let y := 20 in
  reflexivity.
Qed.
Lean
def complexCalc (x y : Nat) : Nat :=
  let sum := x + y
  let prod := x * y
  sum + prod

-- 更简洁的写法
def complexCalc' (x y : Nat) : Nat :=
  let sum := x + y; let prod := x * y; sum + prod
递归函数
Coq - 使用 Fixpoint
Fixpoint factorial (n : nat) : nat :=
  match n with
  | 0 => 1
  | S n' => n * factorial n'
  end.

Fixpoint sum_list (l : list nat) : nat :=
  match l with
  | [] => 0
  | x :: xs => x + sum_list xs
  end.
Lean - 自动终止性检查
def factorial : Nat → Nat
  | 0 => 1
  | n + 1 => (n + 1) * factorial n

def sumList : List Nat → Nat
  | [] => 0
  | x :: xs => x + sumList xs

关键点:所有递归函数必须是可终止的。Coq 使用 Fixpoint 明确标记递归,Lean 自动检测并验证终止性。

习题 1.3.3

练习使用 Print/#print 和 Search/#find 命令:

  1. 查看自然数类型的定义
  2. 搜索关于加法交换律的定理
  3. 查看列表的 append (++) 操作的定义
习题 1.3.4

定义一个递归函数 sum_to_n,计算从 0 到 n 的所有自然数之和。

习题 1.3.5

使用 let 定义一个函数,计算一个数的平方加上这个数本身。

1.4 基本类型

Coq 和 Lean 都提供了丰富的类型系统。理解这些类型的本质——它们都是通过归纳定义构造的——是掌握定理证明器的关键。

自然数 (nat / Nat)

归纳定义的本质
Coq
(* 查看 nat 的定义 *)
Print nat.
(* Inductive nat : Set :=
     | O : nat              (* 零 *)
     | S : nat -> nat       (* 后继函数 *)
*)

(* 自然数的构造 *)
Check O.          (* O : nat *)
Check S O.        (* S O : nat,表示 1 *)
Check S (S O).    (* S (S O) : nat,表示 2 *)
Lean
-- 查看 Nat 的定义
#print Nat
-- inductive Nat : Type
--   | zero : Nat
--   | succ : Nat → Nat

-- 自然数的构造
#check Nat.zero         -- Nat.zero : Nat
#check Nat.succ 0       -- Nat.succ 0 : Nat (表示 1)
#check Nat.succ 1       -- Nat.succ 1 : Nat (表示 2)
使用和运算
Coq
(* 字面量 *)
Check 42.         (* 42 : nat *)

(* 运算 *)
Compute 3 + 4.    (* = 7 : nat *)
Compute 5 - 2.    (* = 3 : nat *)
Compute 3 * 4.    (* = 12 : nat *)
Compute 5 - 8.    (* = 0 : nat (饱和减法) *)
Compute 10 / 3.   (* = 3 : nat (整数除法) *)
Compute 10 mod 3. (* = 1 : nat (取模) *)
Lean
-- 字面量
#check 42         -- 42 : Nat

-- 运算
#eval 3 + 4       -- 7
#eval 5 - 2       -- 3
#eval 3 * 4       -- 12
#eval 5 - 8       -- 0 (饱和减法)
#eval 10 / 3      -- 3 (整数除法)
#eval 10 % 3      -- 1 (取模)

布尔值 (bool / Bool)

定义和使用
Coq
(* 查看定义 *)
Print bool.
(* Inductive bool : Set :=
     | true : bool
     | false : bool *)

(* 布尔运算 *)
Compute negb true.           (* = false : bool *)
Compute true && false.       (* = false : bool *)
Compute true || false.       (* = true : bool *)
Compute xorb true false.     (* = true : bool *)
Lean
-- 查看定义
#print Bool
-- inductive Bool : Type
--   | false : Bool
--   | true : Bool

-- 布尔运算
#eval not true              -- false
#eval true && false         -- false
#eval true || false         -- true
#eval xor true false        -- true

列表 (list / List)

定义和构造
Coq
Require Import List.
Import ListNotations.

(* 查看定义 *)
Print list.
(* Inductive list (A : Type) : Type :=
     | nil : list A
     | cons : A -> list A -> list A *)

(* 列表构造 *)
Check @nil nat.              (* nil : list nat *)
Check cons 1 (cons 2 nil).   (* cons 1 (cons 2 nil) : list nat *)
Check [1; 2; 3].            (* 语法糖 *)
Lean
-- 查看定义
#print List
-- inductive List (α : Type u) : Type u
--   | nil : List α
--   | cons : α → List α → List α

-- 列表构造
#check @List.nil Nat         -- List.nil : List Nat
#check List.cons 1 List.nil  -- List.cons 1 List.nil : List Nat
#check [1, 2, 3]            -- 语法糖(注意:逗号而非分号)
列表操作
Coq
(* 列表操作 *)
Compute length [1; 2; 3; 4].        (* = 4 : nat *)
Compute [1; 2] ++ [3; 4].          (* = [1; 2; 3; 4] : list nat *)
Compute rev [1; 2; 3].             (* = [3; 2; 1] : list nat *)
Compute hd 0 [1; 2; 3].            (* = 1 : nat *)
Compute tl [1; 2; 3].              (* = [2; 3] : list nat *)
Lean
-- 列表操作
#eval List.length [1, 2, 3, 4]     -- 4
#eval [1, 2] ++ [3, 4]             -- [1, 2, 3, 4]
#eval List.reverse [1, 2, 3]       -- [3, 2, 1]
#eval List.head! [1, 2, 3]         -- 1
#eval List.tail! [1, 2, 3]         -- [2, 3]

更多常用类型

字符串 (String)
Coq
Require Import String.

Check "Hello".                      (* "Hello" : string *)
Compute "Hello" ++ " " ++ "World". (* = "Hello World" : string *)
Compute String.length "Coq".        (* = 3 : nat *)
Lean
#check "Hello"                      -- "Hello" : String
#eval "Hello" ++ " " ++ "World"    -- "Hello World"
#eval String.length "Lean"          -- 4
积类型 (Product Types)
Coq
(* 有序对 *)
Check (3, true).              (* (3, true) : nat * bool *)
Check (1, "hello", false).    (* 三元组 *)

(* 解构 *)
Definition fst_of_pair (p : nat * bool) : nat :=
  match p with
  | (x, y) => x
  end.
Lean
-- 有序对
#check (3, true)              -- (3, true) : Nat × Bool
#check (1, "hello", false)    -- 三元组

-- 解构
def fstOfPair (p : Nat × Bool) : Nat :=
  match p with
  | (x, y) => x
和类型 (Sum Types)
Coq
(* Either nat 或 bool *)
Check inl 42 : nat + bool.      (* 左注入 *)
Check inr true : nat + bool.    (* 右注入 *)

(* 模式匹配 *)
Definition sum_to_nat (x : nat + bool) : nat :=
  match x with
  | inl n => n
  | inr b => if b then 1 else 0
  end.
Lean
-- Sum 类型
#check Sum.inl 42 : Sum Nat Bool    -- 左注入
#check Sum.inr true : Sum Nat Bool  -- 右注入

-- 模式匹配
def sumToNat (x : Sum Nat Bool) : Nat :=
  match x with
  | Sum.inl n => n
  | Sum.inr b => if b then 1 else 0
习题 1.4.1

预测以下表达式的结果,然后验证:

Coq
Compute S (S (S O)) + S (S O).
Compute 10 - 15.
Compute negb (true && false).
Compute [1; 2] ++ [3; 4].
Lean
#eval Nat.succ 2 + 2
#eval 10 - 15
#eval not (true && false)
#eval [1, 2] ++ [3, 4]
习题 1.4.2

定义一个函数 is_zero,检查一个自然数是否为零。

习题 1.4.3

编写一个函数,交换有序对的两个元素。

习题 1.4.4

使用 Print/#print 查看列表的 map 函数定义,然后使用它将列表中每个元素加倍。

1.5 简单证明

证明是 Coq 和 Lean 的核心功能。本节介绍最基本的证明技术,展示交互式证明的工作流程。

证明结构对比

Coq 的证明结构
Theorem theorem_name : proposition.
Proof.
  (* 策略1 *)
  (* 策略2 *)
  (* ... *)
Qed.

(* 或者使用 Definition 直接给出证明项 *)
Definition theorem_name' : proposition := proof_term.
Lean 的证明结构
-- 策略模式
theorem theorem_name : proposition := by
  -- 策略1
  -- 策略2
  -- ...

-- 项模式(直接给出证明)
theorem theorem_name' : proposition := proof_term

基本策略对比

策略 1:反射性 (reflexivity / rfl)

用于证明形如 x = x 的等式。

Coq
Theorem zero_equals_zero : 0 = 0.
Proof.
  reflexivity.
Qed.

Theorem computation : 2 + 2 = 4.
Proof.
  reflexivity.  (* 自动计算后应用反射性 *)
Qed.
Lean
theorem zero_equals_zero : 0 = 0 := by
  rfl

theorem computation : 2 + 2 = 4 := by
  rfl  -- 自动计算后应用反射性
策略 2:引入假设 (intros / intro)

用于处理全称量词(∀)和蕴含(→)。

Coq
Theorem identity_function : forall x : nat, x = x.
Proof.
  intros x.     (* 引入任意 x *)
  reflexivity.
Qed.

Theorem implication_intro : forall P Q : Prop, P -> Q -> P.
Proof.
  intros P Q.   (* 引入命题 P 和 Q *)
  intros HP HQ. (* 引入假设 HP : P 和 HQ : Q *)
  exact HP.     (* 使用假设 HP *)
Qed.
Lean
theorem identity_function : ∀ x : Nat, x = x := by
  intro x      -- 引入任意 x
  rfl

theorem implication_intro : ∀ P Q : Prop, P → Q → P := by
  intro P Q    -- 引入命题 P 和 Q
  intro HP HQ  -- 引入假设 HP : P 和 HQ : Q
  exact HP     -- 使用假设 HP
策略 3:化简 (simpl / simp)

简化表达式,展开定义。

Coq
Definition double (n : nat) : nat := n + n.

Theorem double_zero : double 0 = 0.
Proof.
  simpl.       (* 展开 double 的定义:0 + 0 *)
  reflexivity.
Qed.
Lean
def double (n : Nat) : Nat := n + n

theorem double_zero : double 0 = 0 := by
  simp [double]  -- 展开 double 的定义
  -- 目标自动被简化为 0 = 0,simp 完成证明

注意:Lean 的 simp 比 Coq 的 simpl 更强大,经常可以自动完成证明。

策略 4:重写 (rewrite / rw)

使用等式重写目标或假设。

Coq
Theorem rewrite_example : forall a b c : nat,
  a = b -> b = c -> a = c.
Proof.
  intros a b c H1 H2.
  rewrite H1.    (* 将目标中的 a 替换为 b *)
  rewrite H2.    (* 将目标中的 b 替换为 c *)
  reflexivity.
Qed.
Lean
theorem rewrite_example : ∀ a b c : Nat,
  a = b → b = c → a = c := by
  intro a b c H1 H2
  rw [H1]        -- 将目标中的 a 替换为 b
  rw [H2]        -- 将目标中的 b 替换为 c
  -- 或者一次性:rw [H1, H2]

完整的证明示例

证明:0 + n = n

这是一个简单但经典的例子,展示了完整的证明流程。

Coq
Theorem zero_plus_n : forall n : nat, 0 + n = n.
Proof.
  intros n.      (* 目标:0 + n = n *)
  simpl.         (* 根据加法定义,0 + n 简化为 n *)
  reflexivity.   (* n = n *)
Qed.
Lean
theorem zero_plus_n : ∀ n : Nat, 0 + n = n := by
  intro n        -- 目标:0 + n = n
  simp           -- 自动简化并完成证明
  
-- 或者更明确地:
theorem zero_plus_n' : ∀ n : Nat, 0 + n = n := by
  intro n
  rfl            -- 0 + n 定义上就等于 n
习题 1.5.1

证明:2 + 3 = 5

习题 1.5.2

证明布尔值的双重否定定律:∀ b : Bool, not (not b) = b

习题 1.5.3

证明简单的逻辑蕴含:如果 P 成立,那么 P 或 Q 成立。

习题 1.5.4

使用 rewrite 证明等式的传递性(已在示例中展示)。

习题 1.5.5

证明:∀ n : Nat, n = n + 0(注意:这与 n + 0 = n 不同!)

第二章:核心概念

2.1 归纳类型

归纳类型是 Coq 和 Lean 类型系统的核心。通过归纳定义,我们可以构造新的数据类型。

归纳定义基础对比

Coq 版本
(* 定义一个简单的归纳类型:日期 *)
Inductive day : Type :=
  | monday
  | tuesday
  | wednesday
  | thursday
  | friday
  | saturday
  | sunday.
Lean 版本
-- 定义一个简单的归纳类型:日期
inductive Day where
  | monday
  | tuesday
  | wednesday
  | thursday
  | friday
  | saturday
  | sunday
使用归纳类型
Coq 版本
(* 定义函数处理归纳类型 *)
Definition next_weekday (d : day) : day :=
  match d with
  | monday    => tuesday
  | tuesday   => wednesday
  | wednesday => thursday
  | thursday  => friday
  | friday    => monday
  | saturday  => monday
  | sunday    => monday
  end.

(* 测试 *)
Compute next_weekday friday.   (* = monday : day *)
Compute next_weekday saturday. (* = monday : day *)
Lean 版本
-- 定义函数处理归纳类型
def nextWeekday (d : Day) : Day :=
  match d with
  | .monday    => .tuesday
  | .tuesday   => .wednesday
  | .wednesday => .thursday
  | .thursday  => .friday
  | .friday    => .monday
  | .saturday  => .monday
  | .sunday    => .monday

-- 测试
#eval nextWeekday .friday   -- .monday
#eval nextWeekday .saturday -- .monday

带参数的归纳类型

(* RGB 颜色类型 *)
Inductive rgb : Type :=
  | red
  | green
  | blue.

(* 带有一个颜色的归纳类型 *)
Inductive color : Type :=
  | black
  | white
  | primary (p : rgb).
习题 2.1.1

定义一个归纳类型 bool3,它有三个值:yesnomaybe

递归归纳类型

(* 自然数的归纳定义 *)
Inductive nat : Type :=
  | O : nat
  | S : nat -> nat.

(* 二叉树的归纳定义 *)
Inductive bin_tree : Type :=
  | leaf : bin_tree
  | node : nat -> bin_tree -> bin_tree -> bin_tree.
列表的归纳定义
Inductive natlist : Type :=
  | nil
  | cons (n : nat) (l : natlist).

(* 使用记号让列表更易读 *)
Notation "x :: l" := (cons x l)
                     (at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).

(* 现在可以这样写列表 *)
Definition mylist := [1; 2; 3].

(* 列表函数 *)
Fixpoint length (l : natlist) : nat :=
  match l with
  | nil => 0
  | h :: t => S (length t)
  end.
习题 2.1.2

定义一个函数 repeat,接受一个自然数 n 和一个次数 count,返回包含 countn 的列表。

习题 2.1.3

定义一个归纳类型 natprod 表示自然数对,并定义函数 fstsnd 来提取第一个和第二个元素。

2.2 函数定义

Coq 和 Lean 都支持多种函数定义方式,包括简单函数、递归函数和高阶函数。

递归函数对比

Coq - Fixpoint
(* 阶乘函数 *)
Fixpoint factorial (n : nat) : nat :=
  match n with
  | O => 1
  | S n' => n * factorial n'
  end.

(* 斐波那契数列 *)
Fixpoint fib (n : nat) : nat :=
  match n with
  | O => O
  | S O => 1
  | S (S n'') => fib (S n'') + fib n''
  end.
Lean - def/rec
-- 阶乘函数
def factorial : Nat → Nat
  | 0 => 1
  | n + 1 => (n + 1) * factorial n

-- 斐波那契数列
def fib : Nat → Nat
  | 0 => 0
  | 1 => 1
  | n + 2 => fib (n + 1) + fib n
列表上的递归函数
Fixpoint append (l1 l2 : natlist) : natlist :=
  match l1 with
  | nil => l2
  | h :: t => h :: (append t l2)
  end.

Fixpoint rev (l : natlist) : natlist :=
  match l with
  | nil => nil
  | h :: t => append (rev t) [h]
  end.

(* 使用记号 *)
Notation "x ++ y" := (append x y)
                     (right associativity, at level 60).

模式匹配的高级用法

(* 同时匹配多个值 *)
Definition both_zero (n m : nat) : bool :=
  match n, m with
  | O, O => true
  | _, _ => false
  end.

(* 嵌套模式匹配 *)
Definition silly_fun (n : nat) : nat :=
  match n with
  | O => O
  | S (S (S O)) => S (S O)  (* 特殊处理 3 *)
  | S n' => n'
  end.
习题 2.2.1

定义一个函数 sum_list,计算列表中所有元素的和。

习题 2.2.2

定义一个函数 nth_default,返回列表的第 n 个元素,如果 n 超出范围则返回默认值。

高阶函数基础

(* 函数作为参数 *)
Definition do_twice (f : nat -> nat) (n : nat) : nat :=
  f (f n).

(* 测试 *)
Definition plus3 (n : nat) : nat := n + 3.
Compute do_twice plus3 0. (* = 6 : nat *)

(* 返回函数的函数 *)
Definition plus_n : nat -> (nat -> nat) :=
  fun n => fun m => n + m.

(* 柯里化 *)
Definition plus_3 := plus_n 3.
Compute plus_3 4. (* = 7 : nat *)
习题 2.2.3

定义一个高阶函数 filter,它接受一个谓词函数和一个列表,返回满足谓词的元素列表。

2.3 证明策略(基础)

本节介绍 Coq 和 Lean 中最常用的证明策略(tactics)。

引入变量对比

Coq - intros/intro
Theorem example_intros : forall n m : nat, n + m = m + n.
Proof.
  intros n m.  (* 引入两个任意自然数 n 和 m *)
  (* 现在目标变成了证明 n + m = m + n *)
Admitted.

Theorem example_intro : forall n : nat, n = n.
Proof.
  intro n.     (* intro 只引入一个变量 *)
  reflexivity.
Qed.
Lean - intro
theorem example_intros : ∀ n m : Nat, n + m = m + n := by
  intro n m  -- 引入两个任意自然数 n 和 m
  -- 现在目标变成了证明 n + m = m + n
  sorry

theorem example_intro : ∀ n : Nat, n = n := by
  intro n    -- intro 引入一个变量
  rfl        -- reflexivity

apply 和 exact

(* 假设我们已经证明了一个引理 *)
Theorem plus_0_r : forall n : nat, n + 0 = n.
Proof. (* 证明省略 *) Admitted.

(* 使用 apply *)
Theorem apply_example : forall n : nat, (n + 0) + 0 = n.
Proof.
  intros n.
  apply plus_0_r.    (* 使用 plus_0_r 简化目标 *)
  apply plus_0_r.    (* 再次使用 *)
Qed.

(* 使用 exact *)
Theorem exact_example : 42 = 42.
Proof.
  exact (eq_refl 42). (* 直接给出证明项 *)
Qed.

destruct 分情况讨论

对布尔值分情况
Theorem bool_fn_applied_twice :
  forall (f : bool -> bool) (b : bool),
  f (f (f (f b))) = f (f b).
Proof.
  intros f b.
  destruct b.
  - (* b = true *)
    destruct (f true) eqn:Hft.
    + (* f true = true *)
      rewrite Hft. rewrite Hft. reflexivity.
    + (* f true = false *)
      destruct (f false) eqn:Hff.
      * rewrite Hff. reflexivity.
      * rewrite Hft. rewrite Hff. reflexivity.
  - (* b = false *)
    destruct (f false) eqn:Hff.
    + (* f false = true *)
      destruct (f true) eqn:Hft.
      * rewrite Hft. reflexivity.
      * rewrite Hff. rewrite Hft. reflexivity.
    + (* f false = false *)
      rewrite Hff. rewrite Hff. reflexivity.
Qed.

rewrite 重写

Theorem rewrite_example : forall n m : nat,
  n = m -> n + n = m + m.
Proof.
  intros n m H.
  rewrite H.      (* 使用假设 H: n = m 重写目标 *)
  reflexivity.
Qed.

(* 反向重写 *)
Theorem rewrite_backwards : forall n m : nat,
  n = m -> m + m = n + n.
Proof.
  intros n m H.
  rewrite <- H.   (* 反向使用 H *)
  reflexivity.
Qed.
习题 2.3.1

证明:forall b c : bool, b = c -> negb b = negb c

习题 2.3.2

证明:forall b : bool, b && true = b

习题 2.3.3

使用 destruct 证明:forall n : nat, n =? n = true

高级策略:inversion

inversion 是一个强大的策略,用于分析归纳类型的构造子。它可以:

  • 分析等式中的构造子
  • 推导出隐含的等式
  • 排除不可能的情况
(* inversion 基本用法 *)
Theorem zero_not_succ : forall n : nat,
  0 <> S n.
Proof.
  intros n H.
  inversion H.  (* 矛盾:0 和 S n 有不同的构造子 *)
Qed.

(* 从构造子等式推导参数等式 *)
Theorem succ_injective : forall n m : nat,
  S n = S m -> n = m.
Proof.
  intros n m H.
  inversion H.    (* 从 S n = S m 推导出 n = m *)
  reflexivity.
Qed.

(* 分析列表 *)
Theorem cons_injective : forall (A : Type) (x y : A) (l1 l2 : list A),
  x :: l1 = y :: l2 -> x = y /\ l1 = l2.
Proof.
  intros A x y l1 l2 H.
  inversion H.
  split; reflexivity.
Qed.

高级策略:injection 和 discriminate

injection 从构造子的等式中提取参数的等式,discriminate 证明不同构造子的不等式。

(* injection: 从构造子等式提取参数等式 *)
Theorem injection_ex : forall n m : nat,
  S n = S m -> n = m.
Proof.
  intros n m H.
  injection H.    (* 得到 n = m *)
  intros H'.
  assumption.
Qed.

(* discriminate: 证明不同构造子不相等 *)
Theorem discriminate_ex : forall n : nat,
  0 = S n -> False.
Proof.
  intros n H.
  discriminate H.  (* 0 和 S n 有不同构造子 *)
Qed.

(* 组合使用 *)
Theorem nat_cases : forall n : nat,
  n = 0 \/ exists m, n = S m.
Proof.
  intros n.
  destruct n.
  - left. reflexivity.
  - right. exists n. reflexivity.
Qed.

归纳策略的高级用法

(* 对任意变量进行归纳 *)
Theorem plus_comm : forall n m : nat,
  n + m = m + n.
Proof.
  intros n m.
  induction n as [| n' IHn'].
  - (* n = 0 *)
    simpl. 
    (* 需要 m + 0 = m 的引理 *)
    admit.
  - (* n = S n' *)
    simpl.
    rewrite IHn'.
    (* 需要 S (m + n') = m + S n' 的引理 *)
    admit.
Admitted.

(* 同时对多个变量归纳 *)
Theorem eq_dec : forall n m : nat,
  {n = m} + {n <> m}.
Proof.
  intros n.
  induction n as [| n' IHn'];
  intros m;
  destruct m as [| m'].
  - (* n = 0, m = 0 *)
    left. reflexivity.
  - (* n = 0, m = S m' *)
    right. discriminate.
  - (* n = S n', m = 0 *)
    right. discriminate.
  - (* n = S n', m = S m' *)
    destruct (IHn' m') as [H | H].
    + left. rewrite H. reflexivity.
    + right. intros Heq. 
      injection Heq. intros. 
      contradiction.
Qed.

(* 归纳假设的命名 *)
Theorem named_induction : forall n : nat,
  n * 0 = 0.
Proof.
  intros n.
  induction n as [| n' IH_n_times_0].
  - reflexivity.
  - simpl. 
    exact IH_n_times_0.
Qed.

Lean 中的对应策略

-- Lean 中的 cases (类似 destruct)
example (b : Bool) : b = true ∨ b = false := by
  cases b
  · left; rfl
  · right; rfl

-- Lean 中的 induction
example (n : Nat) : n + 0 = n := by
  induction n with
  | zero => rfl
  | succ n' ih => 
    simp [Nat.add_succ]
    exact ih

-- Lean 中的 injection
example (n m : Nat) (h : n.succ = m.succ) : n = m := by
  injection h

-- Lean 中的矛盾
example (h : 0 = 1) : False := by
  contradiction
习题 2.3.4

使用 inversion 证明:forall n m : nat, S n = S m -> n = m

习题 2.3.5

使用 discriminate 证明:forall n : nat, true <> false

2.4 命题逻辑

Coq 中的命题逻辑包括合取、析取、蕴含、否定和量词。

合取(与)

(* 合取的定义 *)
Inductive and (P Q : Prop) : Prop :=
  | conj : P -> Q -> and P Q.

(* 使用符号 /\ *)
Notation "P /\ Q" := (and P Q) : type_scope.

(* 证明合取 *)
Theorem and_intro : forall P Q : Prop,
  P -> Q -> P /\ Q.
Proof.
  intros P Q HP HQ.
  split.           (* 将目标 P /\ Q 分解为两个子目标 *)
  - assumption.    (* 证明 P *)
  - assumption.    (* 证明 Q *)
Qed.

(* 使用合取 *)
Theorem and_proj1 : forall P Q : Prop,
  P /\ Q -> P.
Proof.
  intros P Q H.
  destruct H as [HP HQ].  (* 分解假设 *)
  assumption.
Qed.

析取(或)

(* 析取的定义 *)
Inductive or (P Q : Prop) : Prop :=
  | or_introl : P -> or P Q
  | or_intror : Q -> or P Q.

(* 使用符号 \/ *)
Notation "P \/ Q" := (or P Q) : type_scope.

(* 证明析取 *)
Theorem or_intro_l : forall P Q : Prop,
  P -> P \/ Q.
Proof.
  intros P Q HP.
  left.            (* 选择左边 *)
  assumption.
Qed.

(* 对析取分情况 *)
Theorem or_commut : forall P Q : Prop,
  P \/ Q -> Q \/ P.
Proof.
  intros P Q H.
  destruct H as [HP | HQ].
  - (* P 成立 *)
    right. assumption.
  - (* Q 成立 *)
    left. assumption.
Qed.

蕴含和否定

(* 蕴含就是函数类型 *)
Theorem modus_ponens : forall P Q : Prop,
  P -> (P -> Q) -> Q.
Proof.
  intros P Q HP HPQ.
  apply HPQ.
  assumption.
Qed.

(* 否定的定义 *)
Definition not (P : Prop) : Prop := P -> False.
Notation "~ P" := (not P) : type_scope.

(* 矛盾律 *)
Theorem contradiction : forall P : Prop,
  P -> ~P -> False.
Proof.
  intros P HP HnP.
  apply HnP.
  assumption.
Qed.

量词

全称量词
Theorem forall_example : 
  forall n : nat, n + 0 = n -> S n + 0 = S n.
Proof.
  intros n H.
  simpl.
  rewrite H.
  reflexivity.
Qed.
存在量词
Theorem exists_example : 
  exists n : nat, n + 3 = 5.
Proof.
  exists 2.      (* 给出见证 *)
  reflexivity.
Qed.

(* 使用存在量词 *)
Theorem exists_proj : forall P : nat -> Prop,
  (exists n, P n) -> exists m, P m.
Proof.
  intros P H.
  destruct H as [n Hn].  (* 获取见证和证明 *)
  exists n.
  assumption.
Qed.
习题 2.4.1

证明德摩根定律:forall P Q : Prop, ~(P \/ Q) -> ~P /\ ~Q

习题 2.4.2

证明:forall n m : nat, n = m \/ n <> m(需要引入排中律)

2.5 归纳证明

归纳法是 Coq 中证明关于递归数据类型性质的核心技术。

对自然数的归纳

Theorem plus_n_O : forall n : nat,
  n + 0 = n.
Proof.
  intros n.
  induction n as [| n' IHn'].
  - (* n = 0 *)
    reflexivity.
  - (* n = S n' *)
    simpl.
    rewrite IHn'.    (* 使用归纳假设 *)
    reflexivity.
Qed.
加法交换律的证明
(* 首先需要一些辅助引理 *)
Theorem plus_0_n : forall n : nat,
  0 + n = n.
Proof.
  intros n.
  reflexivity.  (* 根据加法的定义,0 + n 直接计算为 n *)
Qed.

Theorem plus_n_Sm : forall n m : nat,
  S (n + m) = n + (S m).
Proof.
  intros n m.
  induction n as [| n' IHn'].
  - reflexivity.
  - simpl. rewrite IHn'. reflexivity.
Qed.

(* 现在可以证明交换律 *)
Theorem plus_comm : forall n m : nat,
  n + m = m + n.
Proof.
  intros n m.
  induction n as [| n' IHn'].
  - (* n = 0: 需要证明 0 + m = m + 0 *)
    rewrite plus_0_n.    (* 0 + m = m *)
    rewrite <- plus_n_O. (* m + 0 = m *)
    reflexivity.
  - (* n = S n': 需要证明 S n' + m = m + S n' *)
    simpl.               (* S n' + m = S (n' + m) *)
    rewrite IHn'.        (* S (n' + m) = S (m + n') *)
    rewrite plus_n_Sm.   (* S (m + n') = m + S n' *)
    reflexivity.
Qed.

对列表的归纳

Theorem app_assoc : forall l1 l2 l3 : natlist,
  (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
  intros l1 l2 l3.
  induction l1 as [| h l1' IHl1'].
  - (* l1 = nil *)
    reflexivity.
  - (* l1 = h :: l1' *)
    simpl.
    rewrite IHl1'.
    reflexivity.
Qed.

Theorem rev_app_distr : forall l1 l2 : natlist,
  rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof.
  intros l1 l2.
  induction l1 as [| h l1' IHl1'].
  - (* l1 = nil *)
    simpl.
    rewrite app_nil_r.  (* 假设已证明 *)
    reflexivity.
  - (* l1 = h :: l1' *)
    simpl.
    rewrite IHl1'.
    rewrite app_assoc.
    reflexivity.
Qed.

强归纳法

(* 强归纳原理的例子 *)
Theorem strong_induction :
  forall P : nat -> Prop,
  (forall n : nat, (forall m : nat, m < n -> P m) -> P n) ->
  forall n : nat, P n.
Proof.
  (* 证明略,这是一个高级话题 *)
Admitted.
习题 2.5.1

证明:forall n : nat, n + n = 2 * n

习题 2.5.2

证明列表反转的恒等性:forall l : natlist, rev (rev l) = l

2.6 证明自动化

Coq 提供了多种自动化策略,可以大大简化证明过程。这些策略能够自动尝试应用各种证明技术。

auto 策略

auto 是最基本的自动化策略,它尝试使用 assumptionreflexivityapply 等基本策略的组合。

(* auto 的基本用法 *)
Theorem auto_example : forall P Q R : Prop,
  (P -> Q) -> (Q -> R) -> P -> R.
Proof.
  auto.  (* 自动完成证明 *)
Qed.

(* auto 使用深度参数 *)
Theorem auto_depth : forall P Q R S : Prop,
  (P -> Q) -> (Q -> R) -> (R -> S) -> P -> S.
Proof.
  auto 4.  (* 搜索深度为 4 *)
Qed.

(* auto 使用提示数据库 *)
Hint Resolve plus_comm : core.
Hint Resolve plus_assoc : core.

Theorem auto_with_hints : forall n m p : nat,
  n + (m + p) = (m + n) + p.
Proof.
  auto.  (* 使用提示数据库中的定理 *)
Qed.

eauto 策略

eautoauto 的增强版本,它还会尝试使用存在量词实例化。

(* eauto 处理存在量词 *)
Theorem eauto_example : forall P : nat -> Prop,
  P 42 -> exists n, P n.
Proof.
  eauto.  (* 自动找到见证 42 *)
Qed.

(* eauto 的高级用法 *)
Theorem eauto_complex : forall P Q : nat -> Prop,
  (forall n, P n -> Q (S n)) ->
  P 5 ->
  exists m, Q m.
Proof.
  eauto.  (* 自动构造 Q 6 *)
Qed.

tauto 策略

tauto 是专门用于命题逻辑的完备决策过程。

(* tauto 解决命题逻辑 *)
Theorem tauto_example : forall P Q : Prop,
  P /\ (P -> Q) -> Q.
Proof.
  tauto.
Qed.

(* 复杂的命题逻辑 *)
Theorem tauto_complex : forall P Q R : Prop,
  (P \/ Q) /\ (~P \/ R) /\ (~Q \/ R) -> R.
Proof.
  tauto.
Qed.

(* tauto 不能处理量词 *)
Theorem tauto_limitation : forall n : nat,
  n = n.
Proof.
  (* tauto. *) (* 会失败 *)
  auto.        (* 但 auto 可以 *)
Qed.

omega/lia 策略

omega(现在推荐使用 lia)用于解决线性整数算术。

Require Import Lia.

(* lia 解决算术不等式 *)
Theorem lia_example : forall n m : nat,
  n < m -> n + 10 < m + 11.
Proof.
  intros.
  lia.
Qed.

(* 复杂的算术约束 *)
Theorem lia_complex : forall x y z : nat,
  x + y = 5 ->
  2 * x + y = 7 ->
  x = 2 /\ y = 3.
Proof.
  intros.
  lia.
Qed.

(* lia 处理多个约束 *)
Theorem lia_system : forall a b c : nat,
  a + b + c = 10 ->
  a = 2 * b ->
  b = c + 1 ->
  a = 4 /\ b = 2 /\ c = 1.
Proof.
  intros.
  lia.
Qed.

firstorder 策略

firstorder 是一个一阶逻辑的自动证明器。

(* firstorder 处理量词 *)
Theorem firstorder_example : forall P Q : nat -> Prop,
  (forall x, P x -> Q x) ->
  (exists x, P x) ->
  exists x, Q x.
Proof.
  firstorder.
Qed.

(* 复杂的一阶逻辑 *)
Theorem firstorder_complex :
  forall P Q R : nat -> nat -> Prop,
  (forall x y, P x y -> Q y x) ->
  (forall x y, Q x y -> R x y) ->
  (forall x y, P x y -> R y x).
Proof.
  firstorder.
Qed.

组合使用自动化策略

(* 尝试多种策略 *)
Ltac solve_goal :=
  auto || tauto || lia || firstorder.

Theorem combined_1 : forall n : nat,
  n + 0 = n.
Proof.
  solve_goal.
Qed.

Theorem combined_2 : forall P Q : Prop,
  P /\ Q -> Q /\ P.
Proof.
  solve_goal.
Qed.

(* 更复杂的组合 *)
Ltac crush :=
  intros;
  repeat match goal with
  | [ H : _ /\ _ |- _ ] => destruct H
  | [ |- _ /\ _ ] => split
  | [ H : _ \/ _ |- _ ] => destruct H
  | [ H : exists _, _ |- _ ] => destruct H
  end;
  auto.

Theorem crush_example : forall P Q R : Prop,
  (P /\ Q) \/ (P /\ R) -> P /\ (Q \/ R).
Proof.
  crush.
Qed.

ring 和 field 策略

用于解决环和域上的等式。

Require Import Ring.
Require Import Field.

(* ring 处理多项式等式 *)
Theorem ring_example : forall x y : nat,
  (x + y) * (x + y) = x * x + 2 * x * y + y * y.
Proof.
  intros.
  ring.
Qed.

(* 更复杂的多项式 *)
Theorem ring_complex : forall a b c : nat,
  (a + b + c) * (a + b + c) = 
  a * a + b * b + c * c + 2 * a * b + 2 * a * c + 2 * b * c.
Proof.
  intros.
  ring.
Qed.

Lean 中的自动化策略

-- Lean 的 simp 策略
example (x y : Nat) : x + y = y + x := by
  simp [Nat.add_comm]

-- Lean 的 omega 策略
example (x y : Nat) (h : x < y) : x + 1 ≤ y := by
  omega

-- Lean 的 aesop 策略(类似 auto)
example (p q r : Prop) (h1 : p → q) (h2 : q → r) (h3 : p) : r := by
  aesop

-- Lean 的 ring 策略
example (x y : ℤ) : (x + y)^2 = x^2 + 2*x*y + y^2 := by
  ring
习题 2.6.1

使用自动化策略证明:forall x y z : nat, x + (y + z) = y + (x + z)

习题 2.6.2

使用 firstorder 证明:forall P Q : nat -> Prop, (exists x, P x /\ Q x) -> (exists x, P x) /\ (exists x, Q x)

rev (rev l) = l. Proof. intros l. induction l as [| h l' IHl']. - (* l = nil *) reflexivity. - (* l = h :: l' *) simpl. rewrite rev_app_distr. rewrite IHl'. simpl. reflexivity. Qed.
习题 2.5.3

定义一个函数 count 统计列表中某个元素的出现次数,并证明:count n (l1 ++ l2) = count n l1 + count n l2

第三章:进阶主题

3.1 依赖类型

依赖类型是 Coq 和 Lean 类型系统的核心特性,允许类型依赖于值。这使得我们可以编写更精确的规范。

依赖函数类型对比

Coq 版本
(* 依赖函数类型的例子 *)
Definition identity : forall (A : Type), A -> A :=
  fun A x => x.

(* 类型依赖于参数 *)
Definition zero_or_succ : forall (b : bool), if b then nat else bool :=
  fun b => if b then 0 else false.
Lean 版本
-- 依赖函数类型的例子
def identity : (A : Type) → A → A :=
  fun A x => x

-- 类型依赖于参数
def zeroOrSucc : (b : Bool) → if b then Nat else Bool :=
  fun b => if b then 0 else false
(* 更复杂的例子 *) Definition dependent_pair : forall (n : nat), match n with | 0 => bool | S _ => nat end := fun n => match n with | 0 => true | S m => m end.

依赖对(Σ类型)

(* 使用 sig 定义依赖对 *)
Definition positive_nat : Type := 
  { n : nat | n > 0 }.

(* 构造依赖对 *)
Definition one_positive : positive_nat.
Proof.
  exists 1.
  auto.
Defined.

(* 另一种写法 *)
Definition two_positive : positive_nat :=
  exist _ 2 (gt_Sn_O 1).

(* 提取值和证明 *)
Definition get_value (p : positive_nat) : nat :=
  match p with
  | exist _ n _ => n
  end.

Definition get_proof (p : positive_nat) : get_value p > 0 :=
  match p with
  | exist _ _ proof => proof
  end.

向量(长度索引列表)

向量的定义
Inductive vec (A : Type) : nat -> Type :=
  | vnil : vec A 0
  | vcons : forall n, A -> vec A n -> vec A (S n).

Arguments vnil {A}.
Arguments vcons {A n}.

(* 向量的例子 *)
Definition vec_123 : vec nat 3 :=
  vcons 1 (vcons 2 (vcons 3 vnil)).

(* 安全的头函数 *)
Definition vhead {A : Type} {n : nat} (v : vec A (S n)) : A :=
  match v with
  | vcons _ h _ => h
  end.

(* 安全的尾函数 *)
Definition vtail {A : Type} {n : nat} (v : vec A (S n)) : vec A n :=
  match v with
  | vcons _ _ t => t
  end.
习题 3.1.1

定义一个函数 vmap,对向量中的每个元素应用一个函数。

习题 3.1.2

定义一个函数 vappend,连接两个向量。

依赖模式匹配

(* convoy 模式 *)
Definition vhead' {A : Type} {n : nat} (v : vec A n) : 
  match n with
  | 0 => unit
  | S _ => A
  end :=
  match v in vec _ m return 
    match m with
    | 0 => unit
    | S _ => A
    end
  with
  | vnil => tt
  | vcons _ h _ => h
  end.

(* 使用依赖类型确保安全性 *)
Definition safe_div : forall (n m : nat), m <> 0 -> nat.
  intros n m H.
  exact (n / m).
Defined.
习题 3.1.3

定义一个类型 bounded_nat n,表示小于 n 的自然数,并定义相关操作。

3.2 高阶函数与证明

高阶函数是函数式编程的核心,在 Coq 和 Lean 中它们也是证明的重要工具。

Map、Filter 和 Fold 对比

Coq 版本
Require Import List.
Import ListNotations.

(* map 的定义和性质 *)
Fixpoint map {A B : Type} (f : A -> B) (l : list A) : list B :=
  match l with
  | [] => []
  | h :: t => (f h) :: (map f t)
  end.
Lean 版本
-- map 的定义和性质
def map {α β : Type} (f : α → β) : List α → List β
  | [] => []
  | h :: t => f h :: map f t
Theorem map_length : forall A B (f : A -> B) (l : list A), length (map f l) = length l. Proof. intros A B f l. induction l. - reflexivity. - simpl. rewrite IHl. reflexivity. Qed. (* fold 的定义 *) Fixpoint fold_left {A B : Type} (f : A -> B -> A) (l : list B) (a : A) : A := match l with | [] => a | h :: t => fold_left f t (f a h) end. Fixpoint fold_right {A B : Type} (f : B -> A -> A) (a : A) (l : list B) : A := match l with | [] => a | h :: t => f h (fold_right f a t) end.

函数组合

(* 函数组合 *)
Definition compose {A B C : Type} (g : B -> C) (f : A -> B) : A -> C :=
  fun x => g (f x).

Notation "g ∘ f" := (compose g f) (at level 40, left associativity).

(* 组合的性质 *)
Theorem compose_assoc : forall A B C D (h : C -> D) (g : B -> C) (f : A -> B),
  (h ∘ g) ∘ f = h ∘ (g ∘ f).
Proof.
  intros. reflexivity.
Qed.

(* map 与组合的关系 *)
Theorem map_compose : forall A B C (g : B -> C) (f : A -> B) (l : list A),
  map g (map f l) = map (g ∘ f) l.
Proof.
  intros A B C g f l.
  induction l.
  - reflexivity.
  - simpl. rewrite IHl. reflexivity.
Qed.

高阶谓词

(* 所有元素满足谓词 *)
Fixpoint all {A : Type} (P : A -> bool) (l : list A) : bool :=
  match l with
  | [] => true
  | h :: t => P h && all P t
  end.

(* 存在元素满足谓词 *)
Fixpoint any {A : Type} (P : A -> bool) (l : list A) : bool :=
  match l with
  | [] => false
  | h :: t => P h || any P t
  end.

(* 高阶谓词的性质 *)
Theorem all_forall : forall A (P : A -> bool) (l : list A),
  all P l = true <-> forall x, In x l -> P x = true.
Proof.
  intros A P l.
  split.
  - (* -> *)
    intros H x Hin.
    induction l.
    + inversion Hin.
    + simpl in H. apply andb_prop in H.
      destruct H as [Ha Ht].
      destruct Hin.
      * subst. assumption.
      * apply IHl; assumption.
  - (* <- *)
    intros H.
    induction l.
    + reflexivity.
    + simpl. apply andb_true_intro.
      split.
      * apply H. left. reflexivity.
      * apply IHl. intros x Hin.
        apply H. right. assumption.
Qed.
习题 3.2.1

证明:filter P (l1 ++ l2) = filter P l1 ++ filter P l2

习题 3.2.2

定义 partition 函数,将列表分成满足和不满足谓词的两个列表。

函数外延性

(* 函数外延性公理 *)
Axiom functional_extensionality : forall A B (f g : A -> B),
  (forall x, f x = g x) -> f = g.

(* 使用函数外延性的例子 *)
Theorem compose_id_left : forall A B (f : A -> B),
  (fun x => x) ∘ f = f.
Proof.
  intros A B f.
  apply functional_extensionality.
  intros x. reflexivity.
Qed.

3.3 类型类(Type Classes)

类型类提供了一种组织和使用多态函数的方式,类似于 Haskell 的类型类。

定义类型类

(* 定义 Eq 类型类 *)
Class Eq (A : Type) := {
  eqb : A -> A -> bool;
  eqb_refl : forall x, eqb x x = true;
  eqb_sym : forall x y, eqb x y = eqb y x;
  eqb_trans : forall x y z, 
    eqb x y = true -> eqb y z = true -> eqb x z = true
}.

(* 定义 Show 类型类 *)
Class Show (A : Type) := {
  show : A -> string
}.

(* 定义 Monoid 类型类 *)
Class Monoid (A : Type) := {
  mempty : A;
  mappend : A -> A -> A;
  mappend_assoc : forall x y z, 
    mappend x (mappend y z) = mappend (mappend x y) z;
  mappend_id_l : forall x, mappend mempty x = x;
  mappend_id_r : forall x, mappend x mempty = x
}.

实例声明

(* nat 的 Eq 实例 *)
Instance nat_eq : Eq nat := {
  eqb := Nat.eqb
}.
Proof.
  - apply Nat.eqb_refl.
  - apply Nat.eqb_sym.
  - intros x y z H1 H2.
    apply Nat.eqb_eq in H1.
    apply Nat.eqb_eq in H2.
    subst. apply Nat.eqb_refl.
Defined.

(* list 的 Monoid 实例 *)
Instance list_monoid {A : Type} : Monoid (list A) := {
  mempty := [];
  mappend := @app A
}.
Proof.
  - apply app_assoc.
  - reflexivity.
  - apply app_nil_r.
Defined.

(* nat 的 Show 实例 *)
Require Import String.
Open Scope string_scope.

Instance nat_show : Show nat := {
  show := fun n => 
    match n with
    | 0 => "0"
    | 1 => "1"
    | 2 => "2"
    | _ => "..."
    end
}.

使用类型类

(* 泛型函数使用类型类 *)
Definition elem {A : Type} `{Eq A} (x : A) (l : list A) : bool :=
  fold_right (fun y acc => eqb x y || acc) false l.

(* 上下文约束 *)
Definition show_pair {A B : Type} `{Show A} `{Show B} 
  (p : A * B) : string :=
  "(" ++ show (fst p) ++ ", " ++ show (snd p) ++ ")".

(* 多个约束 *)
Definition show_if_equal {A : Type} `{Eq A} `{Show A} 
  (x y : A) : string :=
  if eqb x y then show x else "not equal".

(* 使用 Monoid *)
Definition concat_all {A : Type} `{Monoid A} (l : list A) : A :=
  fold_right mappend mempty l.
习题 3.3.1

定义一个 Ord 类型类,包含比较操作,并为 nat 实现它。

习题 3.3.2

使用类型类定义一个泛型的 sort 函数。

3.4 自动化证明

Coq 提供了多种自动化策略来简化证明过程。

auto 和 eauto

(* auto 使用基本的证明搜索 *)
Theorem auto_example : forall P Q R : Prop,
  (P -> Q) -> (Q -> R) -> P -> R.
Proof.
  auto.  (* 自动完成证明 *)
Qed.

(* auto 的深度控制 *)
Theorem auto_depth : forall P Q R S : Prop,
  (P -> Q) -> (Q -> R) -> (R -> S) -> P -> S.
Proof.
  auto 4.  (* 搜索深度为 4 *)
Qed.

(* eauto 使用存在量词 *)
Theorem eauto_example : forall (A : Type) (P : A -> Prop),
  (exists x, P x) -> exists y, P y.
Proof.
  eauto.
Qed.

omega 和 lia

Require Import Omega.
Require Import Lia.

(* omega 解决线性算术 *)
Theorem omega_example : forall n m : nat,
  n + m = m + n.
Proof.
  intros. omega.
Qed.

(* lia 是 omega 的现代版本 *)
Theorem lia_example : forall x y z : nat,
  x < y -> y < z -> x + 2 < z + 1.
Proof.
  intros. lia.
Qed.

(* 复杂的线性不等式 *)
Theorem complex_ineq : forall a b c : nat,
  a + b < c -> 2 * a + 2 * b < 2 * c.
Proof.
  intros. lia.
Qed.

ring 和 field

Require Import Ring.
Require Import Field.

(* ring 解决环上的等式 *)
Theorem ring_example : forall x y : nat,
  (x + y) * (x + y) = x * x + 2 * x * y + y * y.
Proof.
  intros. ring.
Qed.

(* 对于有理数域 *)
Require Import QArith.
Open Scope Q_scope.

Theorem field_example : forall x y : Q,
  y <> 0 -> (x + y) / y = x / y + 1.
Proof.
  intros. field. assumption.
Qed.

Hint 数据库

(* 创建自定义 Hint 数据库 *)
Create HintDb mybase.

Theorem my_lemma1 : forall n : nat, n + 0 = n.
Proof. auto. Qed.

Theorem my_lemma2 : forall n : nat, 0 + n = n.
Proof. auto. Qed.

(* 添加到数据库 *)
Hint Resolve my_lemma1 : mybase.
Hint Resolve my_lemma2 : mybase.

(* 使用数据库 *)
Theorem use_hints : forall n m : nat,
  (n + 0) + (0 + m) = n + m.
Proof.
  intros. auto with mybase.
Qed.

(* Hint 的不同模式 *)
Hint Immediate my_lemma1 : mybase.  (* 立即应用 *)
Hint Unfold plus : mybase.          (* 展开定义 *)
Hint Constructors nat : mybase.     (* 使用构造器 *)

Ltac 编程基础

(* 定义自定义策略 *)
Ltac my_auto := 
  intros; 
  repeat (simpl; auto).

(* 条件策略 *)
Ltac break_match :=
  match goal with
  | [ |- context[match ?X with _ => _ end] ] => destruct X
  end.

(* 重复应用 *)
Ltac crush :=
  intros;
  repeat (break_match; simpl; auto).

(* 使用自定义策略 *)
Theorem custom_tactic_example : forall b1 b2 : bool,
  (if b1 then true else false) = b1.
Proof.
  crush.
Qed.

(* 更复杂的 Ltac *)
Ltac inv H := inversion H; clear H; subst.

Ltac find_rewrite :=
  match goal with
  | [ H : ?X = ?Y |- context[?X] ] => rewrite H
  end.

Ltac solve_by_inversion :=
  intros; 
  match goal with
  | [ H : _ |- _ ] => inv H; auto
  end.
习题 3.4.1

编写一个 Ltac 策略 solve_bool,自动解决简单的布尔等式。

习题 3.4.2

创建一个 Hint 数据库来自动证明列表的基本性质。

3.5 归纳-递归类型

本节探讨更复杂的归纳类型定义,包括嵌套归纳、相互归纳和归纳-递归定义。

嵌套归纳类型

(* 嵌套列表 *)
Inductive nested_list (A : Type) : Type :=
  | NNil : nested_list A
  | NCons : A -> nested_list A -> nested_list A
  | NNest : nested_list (nested_list A) -> nested_list A.

(* 展平嵌套列表 *)
Fixpoint flatten {A : Type} (nl : nested_list A) : list A :=
  match nl with
  | NNil _ => []
  | NCons _ x xs => x :: flatten xs
  | NNest _ nls => 
      flat_map flatten (flatten_nested nls)
  end
with flatten_nested {A : Type} (nl : nested_list (nested_list A)) 
  : list (nested_list A) :=
  match nl with
  | NNil _ => []
  | NCons _ x xs => x :: flatten_nested xs
  | NNest _ _ => [] (* 防止无限嵌套 *)
  end.

相互归纳类型

(* 偶数和奇数的相互定义 *)
Inductive even : nat -> Prop :=
  | even_O : even 0
  | even_S : forall n, odd n -> even (S n)
with odd : nat -> Prop :=
  | odd_S : forall n, even n -> odd (S n).

(* 相互递归函数 *)
Fixpoint evenb (n : nat) : bool :=
  match n with
  | 0 => true
  | S n' => oddb n'
  end
with oddb (n : nat) : bool :=
  match n with
  | 0 => false
  | S n' => evenb n'
  end.

(* 证明它们的关系 *)
Theorem even_evenb : forall n,
  even n <-> evenb n = true.
Proof.
  intros n.
  split.
  - intros H. induction H.
    + reflexivity.
    + simpl. assumption.
  - intros H. 
    (* 需要相互归纳,这里省略 *)
Admitted.

归纳族(Inductive Families)

(* 有限集合的表示 *)
Inductive fin : nat -> Type :=
  | F1 : forall n, fin (S n)
  | FS : forall n, fin n -> fin (S n).

(* fin n 有恰好 n 个元素 *)
Definition fin_to_nat {n : nat} (f : fin n) : nat :=
  match f with
  | F1 _ => 0
  | FS _ f' => S (fin_to_nat f')
  end.

(* 类型安全的列表索引 *)
Fixpoint nth_fin {A : Type} {n : nat} 
  (l : vec A n) (i : fin n) : A :=
  match i in fin m return vec A m -> A with
  | F1 n' => fun l => 
      match l with
      | vcons _ h _ => h
      end
  | FS n' i' => fun l =>
      match l with
      | vcons _ _ t => nth_fin t i'
      end
  end l.

归纳-递归定义

(* 良基树:每个节点的子树列表是有限的 *)
Inductive tree (A : Type) : Type :=
  | Leaf : A -> tree A
  | Node : list (tree A) -> tree A.

(* 树的大小 *)
Fixpoint tree_size {A : Type} (t : tree A) : nat :=
  match t with
  | Leaf _ => 1
  | Node ts => S (trees_size ts)
  end
with trees_size {A : Type} (ts : list (tree A)) : nat :=
  match ts with
  | [] => 0
  | t :: ts' => tree_size t + trees_size ts'
  end.

(* 更复杂的例子:带约束的归纳类型 *)
Inductive sorted_list : nat -> Type :=
  | sorted_nil : sorted_list 0
  | sorted_cons : forall (n : nat) (l : sorted_list n),
      n <= (hd n l) -> sorted_list n
with hd (default : nat) {n : nat} (l : sorted_list n) : nat :=
  match l with
  | sorted_nil => default
  | sorted_cons x _ _ => x
  end.

终止性证明

(* 使用 Program 处理复杂的终止性 *)
Require Import Program.

Program Fixpoint merge (l1 l2 : list nat) 
  {measure (length l1 + length l2)} : list nat :=
  match l1, l2 with
  | [], _ => l2
  | _, [] => l1
  | h1 :: t1, h2 :: t2 =>
      if h1 <=? h2 
      then h1 :: merge t1 l2
      else h2 :: merge l1 t2
  end.
Next Obligation.
  simpl. lia.
Defined.
Next Obligation.
  simpl. lia.
Defined.

(* 使用 Function 定义 *)
Require Import Recdef.

Function ack (n m : nat) {wf lt n} : nat :=
  match n with
  | 0 => S m
  | S n' =>
      match m with
      | 0 => ack n' 1
      | S m' => ack n' (ack n m')
      end
  end.
Proof.
  - intros. simpl. auto.
  - intros. simpl. auto.
  - apply lt_wf.
Defined.
习题 3.5.1

定义一个表示 JSON 值的归纳类型。

习题 3.5.2

定义一个归纳谓词 balanced,表示二叉树是平衡的。

习题 3.5.3

使用 Program 定义快速排序,并证明其终止性。

第四章:Coq 实践应用

4.1 数据结构验证

本节展示如何在 Coq 中实现和验证常见的数据结构。

平衡二叉搜索树

(* 二叉搜索树的定义 *)
Inductive BST (A : Type) : Type :=
  | Empty : BST A
  | Node : A -> BST A -> BST A -> BST A.

(* 插入操作 *)
Fixpoint insert {A : Type} `{Ord A} (x : A) (t : BST A) : BST A :=
  match t with
  | Empty _ => Node _ x (Empty _) (Empty _)
  | Node _ y l r =>
      if le x y then Node _ y (insert x l) r
      else Node _ y l (insert x r)
  end.

(* BST 性质的归纳定义 *)
Inductive is_bst {A : Type} `{Ord A} : BST A -> Prop :=
  | bst_empty : is_bst (Empty A)
  | bst_node : forall x l r,
      is_bst l ->
      is_bst r ->
      (forall y, In_bst y l -> le y x = true) ->
      (forall y, In_bst y r -> le x y = true) ->
      is_bst (Node A x l r)
with In_bst {A : Type} : A -> BST A -> Prop :=
  | in_here : forall x l r, In_bst x (Node A x l r)
  | in_left : forall x y l r, In_bst x l -> In_bst x (Node A y l r)
  | in_right : forall x y l r, In_bst x r -> In_bst x (Node A y l r).

(* 插入保持 BST 性质 *)
Theorem insert_preserves_bst : forall A `{Ord A} (x : A) (t : BST A),
  is_bst t -> is_bst (insert x t).
Proof.
  intros A H x t Hbst.
  induction Hbst.
  - simpl. constructor; try constructor; intros y Hin; inversion Hin.
  - simpl. destruct (le x x0) eqn:Hle.
    + constructor.
      * apply IHHbst1.
      * assumption.
      * intros y Hin. (* 证明略 *)
Admitted.

红黑树

(* 颜色定义 *)
Inductive color : Type := Red | Black.

(* 红黑树定义 *)
Inductive RBTree (A : Type) : Type :=
  | Leaf : RBTree A
  | Node : color -> A -> RBTree A -> RBTree A -> RBTree A.

(* 红黑树不变量 *)
Inductive is_red_black {A : Type} : nat -> RBTree A -> Prop :=
  | rb_leaf : is_red_black 1 (Leaf A)
  | rb_red : forall n x l r,
      is_red_black n l ->
      is_red_black n r ->
      is_red_black n (Node A Red x l r)
  | rb_black : forall n x l r,
      is_red_black n l ->
      is_red_black n r ->
      is_red_black (S n) (Node A Black x l r).

(* 没有连续的红节点 *)
Inductive no_red_red {A : Type} : RBTree A -> Prop :=
  | nrr_leaf : no_red_red (Leaf A)
  | nrr_black : forall x l r,
      no_red_red l ->
      no_red_red r ->
      no_red_red (Node A Black x l r)
  | nrr_red : forall x l r,
      no_red_red l ->
      no_red_red r ->
      is_black l ->
      is_black r ->
      no_red_red (Node A Red x l r)
with is_black {A : Type} : RBTree A -> Prop :=
  | black_leaf : is_black (Leaf A)
  | black_node : forall x l r,
      is_black (Node A Black x l r).

函数式队列

双列表队列实现
(* 使用两个列表实现队列 *)
Record queue (A : Type) : Type := mk_queue {
  front : list A;
  rear : list A
}.

(* 队列操作 *)
Definition empty_queue {A : Type} : queue A :=
  mk_queue A [] [].

Definition enqueue {A : Type} (x : A) (q : queue A) : queue A :=
  mk_queue A (front A q) (x :: rear A q).

Definition dequeue {A : Type} (q : queue A) : option (A * queue A) :=
  match front A q with
  | [] => 
      match rev (rear A q) with
      | [] => None
      | h :: t => Some (h, mk_queue A t [])
      end
  | h :: t => Some (h, mk_queue A t (rear A q))
  end.

(* 队列的抽象表示 *)
Definition queue_to_list {A : Type} (q : queue A) : list A :=
  front A q ++ rev (rear A q).

(* 正确性定理 *)
Theorem enqueue_correct : forall A (x : A) (q : queue A),
  queue_to_list (enqueue x q) = queue_to_list q ++ [x].
Proof.
  intros. unfold queue_to_list, enqueue. simpl.
  rewrite rev_cons. rewrite app_assoc. reflexivity.
Qed.
项目 4.1.1

实现一个验证过的最小堆(Min-Heap)数据结构。

4.2 算法正确性

本节展示如何验证经典算法的正确性。

排序算法验证

(* 排序的规范 *)
Definition sorted (l : list nat) : Prop :=
  forall i j, i < j < length l -> 
    nth i l 0 <= nth j l 0.

(* 排列关系 *)
Definition permutation (l1 l2 : list nat) : Prop :=
  forall x, count_occ Nat.eq_dec l1 x = count_occ Nat.eq_dec l2 x.

(* 插入排序的实现 *)
Fixpoint insert_sorted (x : nat) (l : list nat) : list nat :=
  match l with
  | [] => [x]
  | h :: t => 
      if x <=? h then x :: h :: t
      else h :: insert_sorted x t
  end.

Fixpoint insertion_sort (l : list nat) : list nat :=
  match l with
  | [] => []
  | h :: t => insert_sorted h (insertion_sort t)
  end.

(* 正确性证明 *)
Theorem insertion_sort_correct : forall l : list nat,
  sorted (insertion_sort l) /\ 
  permutation l (insertion_sort l).
Proof.
  intros l.
  split.
  - (* 排序性 *)
    induction l.
    + unfold sorted. intros. simpl in H. lia.
    + simpl. (* 证明需要关于 insert_sorted 的引理 *)
Admitted.

(* 归并排序 *)
Fixpoint split {A : Type} (l : list A) : list A * list A :=
  match l with
  | [] => ([], [])
  | [x] => ([x], [])
  | x :: y :: t =>
      let (l1, l2) := split t in
      (x :: l1, y :: l2)
  end.

Fixpoint merge (l1 l2 : list nat) : list nat :=
  match l1, l2 with
  | [], _ => l2
  | _, [] => l1
  | h1 :: t1, h2 :: t2 =>
      if h1 <=? h2 
      then h1 :: merge t1 l2
      else h2 :: merge l1 t2
  end.

Function merge_sort (l : list nat) {measure length l} : list nat :=
  match l with
  | [] => []
  | [x] => [x]
  | _ => 
      let (l1, l2) := split l in
      merge (merge_sort l1) (merge_sort l2)
  end.
Proof.
  (* 终止性证明 *)
  - intros. (* split 产生更小的列表 *)
Admitted.

搜索算法验证

(* 二分搜索 *)
Fixpoint binary_search_aux (l : list nat) (x : nat) 
  (lo hi : nat) (fuel : nat) : option nat :=
  match fuel with
  | O => None
  | S fuel' =>
      if hi <=? lo then None
      else
        let mid := (lo + hi) / 2 in
        match nth_error l mid with
        | None => None
        | Some y =>
            if x =? y then Some mid
            else if x <? y 
                 then binary_search_aux l x lo mid fuel'
                 else binary_search_aux l x (S mid) hi fuel'
        end
  end.

Definition binary_search (l : list nat) (x : nat) : option nat :=
  binary_search_aux l x 0 (length l) (length l).

(* 正确性规范 *)
Theorem binary_search_correct : forall l x,
  sorted l ->
  match binary_search l x with
  | Some i => nth_error l i = Some x
  | None => ~ In x l
  end.
Proof.
  (* 需要对 binary_search_aux 进行归纳 *)
Admitted.

图算法

(* 图的表示 *)
Definition graph := nat -> list nat.

(* 深度优先搜索 *)
Fixpoint dfs_visit (g : graph) (visited : list nat) 
  (v : nat) (fuel : nat) : list nat :=
  match fuel with
  | O => visited
  | S fuel' =>
      if existsb (Nat.eqb v) visited 
      then visited
      else
        let visited' := v :: visited in
        fold_left (fun acc u => dfs_visit g acc u fuel') 
                  (g v) visited'
  end.

(* 路径存在性 *)
Inductive path (g : graph) : nat -> nat -> list nat -> Prop :=
  | path_nil : forall v, path g v v []
  | path_cons : forall u v w p,
      In w (g u) ->
      path g w v p ->
      path g u v (u :: p).

(* DFS 的正确性 *)
Theorem dfs_finds_path : forall g u v fuel,
  fuel >= length (nodes g) ->
  (exists p, path g u v p) <-> 
  In v (dfs_visit g [] u fuel).
Proof.
  (* 证明略 *)
Admitted.
项目 4.2.1

验证 Dijkstra 最短路径算法。

4.3 编译器验证基础

本节介绍如何构建和验证一个简单的编程语言。

简单表达式语言

(* 算术表达式 *)
Inductive aexp : Type :=
  | ANum : nat -> aexp
  | APlus : aexp -> aexp -> aexp
  | AMinus : aexp -> aexp -> aexp
  | AMult : aexp -> aexp -> aexp.

(* 布尔表达式 *)
Inductive bexp : Type :=
  | BTrue : bexp
  | BFalse : bexp
  | BEq : aexp -> aexp -> bexp
  | BLe : aexp -> aexp -> bexp
  | BNot : bexp -> bexp
  | BAnd : bexp -> bexp -> bexp.

(* 求值函数 *)
Fixpoint aeval (a : aexp) : nat :=
  match a with
  | ANum n => n
  | APlus a1 a2 => aeval a1 + aeval a2
  | AMinus a1 a2 => aeval a1 - aeval a2
  | AMult a1 a2 => aeval a1 * aeval a2
  end.

Fixpoint beval (b : bexp) : bool :=
  match b with
  | BTrue => true
  | BFalse => false
  | BEq a1 a2 => aeval a1 =? aeval a2
  | BLe a1 a2 => aeval a1 <=? aeval a2
  | BNot b1 => negb (beval b1)
  | BAnd b1 b2 => beval b1 && beval b2
  end.

(* 优化:常量折叠 *)
Fixpoint fold_constants_aexp (a : aexp) : aexp :=
  match a with
  | ANum n => ANum n
  | APlus a1 a2 =>
      match fold_constants_aexp a1, fold_constants_aexp a2 with
      | ANum n1, ANum n2 => ANum (n1 + n2)
      | a1', a2' => APlus a1' a2'
      end
  | AMinus a1 a2 =>
      match fold_constants_aexp a1, fold_constants_aexp a2 with
      | ANum n1, ANum n2 => ANum (n1 - n2)
      | a1', a2' => AMinus a1' a2'
      end
  | AMult a1 a2 =>
      match fold_constants_aexp a1, fold_constants_aexp a2 with
      | ANum n1, ANum n2 => ANum (n1 * n2)
      | a1', a2' => AMult a1' a2'
      end
  end.

(* 优化的正确性 *)
Theorem fold_constants_aexp_sound : forall a,
  aeval (fold_constants_aexp a) = aeval a.
Proof.
  intros a.
  induction a; simpl; try reflexivity.
  - (* APlus *)
    destruct (fold_constants_aexp a1) eqn:Ha1;
    destruct (fold_constants_aexp a2) eqn:Ha2;
    rewrite <- IHa1, <- IHa2; simpl; reflexivity.
  - (* AMinus 和 AMult 类似 *)
Admitted.

命令式语言

(* 命令语言 *)
Inductive com : Type :=
  | CSkip : com
  | CAss : string -> aexp -> com
  | CSeq : com -> com -> com
  | CIf : bexp -> com -> com -> com
  | CWhile : bexp -> com -> com.

(* 程序状态 *)
Definition state := string -> nat.

(* 更新状态 *)
Definition update (st : state) (x : string) (n : nat) : state :=
  fun y => if string_dec x y then n else st y.

(* 大步操作语义 *)
Inductive ceval : com -> state -> state -> Prop :=
  | E_Skip : forall st,
      ceval CSkip st st
  | E_Ass : forall st a1 n x,
      aeval st a1 = n ->
      ceval (CAss x a1) st (update st x n)
  | E_Seq : forall c1 c2 st st' st'',
      ceval c1 st st' ->
      ceval c2 st' st'' ->
      ceval (CSeq c1 c2) st st''
  | E_IfTrue : forall st st' b c1 c2,
      beval st b = true ->
      ceval c1 st st' ->
      ceval (CIf b c1 c2) st st'
  | E_IfFalse : forall st st' b c1 c2,
      beval st b = false ->
      ceval c2 st st' ->
      ceval (CIf b c1 c2) st st'
  | E_WhileFalse : forall b st c,
      beval st b = false ->
      ceval (CWhile b c) st st
  | E_WhileTrue : forall st st' st'' b c,
      beval st b = true ->
      ceval c st st' ->
      ceval (CWhile b c) st' st'' ->
      ceval (CWhile b c) st st''.

类型系统

(* 简单类型 *)
Inductive ty : Type :=
  | TNat : ty
  | TBool : ty.

(* 类型环境 *)
Definition context := string -> option ty.

(* 表达式的类型判断 *)
Inductive has_type_aexp : context -> aexp -> ty -> Prop :=
  | T_ANum : forall Γ n,
      has_type_aexp Γ (ANum n) TNat
  | T_APlus : forall Γ a1 a2,
      has_type_aexp Γ a1 TNat ->
      has_type_aexp Γ a2 TNat ->
      has_type_aexp Γ (APlus a1 a2) TNat
  (* 其他规则类似 *).

(* 类型安全 *)
Theorem type_safety_aexp : forall Γ a T,
  has_type_aexp Γ a T ->
  (T = TNat /\ exists n, aeval a = n).
Proof.
  intros Γ a T H.
  induction H.
  - split. reflexivity. exists n. reflexivity.
  - destruct IHhas_type_aexp1, IHhas_type_aexp2.
    split. reflexivity.
    destruct H2, H3.
    exists (x + x0). simpl. rewrite H2, H3. reflexivity.
Qed.
项目 4.3.1

为简单命令语言实现一个类型检查器并证明其正确性。

4.4 数学定理证明

本节展示如何在 Coq 中形式化和证明数学定理。

皮亚诺算术

(* 自然数的皮亚诺公理 *)
Module Peano.

Parameter N : Type.
Parameter O : N.
Parameter S : N -> N.

(* 公理 1:O 不是任何数的后继 *)
Axiom peano1 : forall n : N, S n <> O.

(* 公理 2:S 是单射 *)
Axiom peano2 : forall n m : N, S n = S m -> n = m.

(* 公理 3:归纳原理 *)
Axiom peano_ind : forall P : N -> Prop,
  P O ->
  (forall n, P n -> P (S n)) ->
  forall n, P n.

(* 定义加法 *)
Fixpoint plus (n m : N) : N :=
  match n with
  | O => m
  | S n' => S (plus n' m)
  end.

(* 证明加法的性质 *)
Theorem plus_O_r : forall n : N, plus n O = n.
Proof.
  intros n.
  pattern n.
  apply peano_ind.
  - reflexivity.
  - intros n' IH.
    simpl. rewrite IH. reflexivity.
Qed.

End Peano.

群论基础

(* 群的定义 *)
Class Group (G : Type) := {
  op : G -> G -> G;
  e : G;
  inv : G -> G;
  
  assoc : forall a b c, op a (op b c) = op (op a b) c;
  id_l : forall a, op e a = a;
  id_r : forall a, op a e = a;
  inv_l : forall a, op (inv a) a = e;
  inv_r : forall a, op a (inv a) = e
}.

Notation "a * b" := (op a b) (at level 40, left associativity).

(* 群的基本定理 *)
Section GroupTheorems.
  Context {G : Type} `{Group G}.
  
  (* 单位元唯一 *)
  Theorem unique_identity : forall e',
    (forall a, e' * a = a) -> e' = e.
  Proof.
    intros e' H0.
    rewrite <- (id_r e').
    rewrite <- (H0 e).
    reflexivity.
  Qed.
  
  (* 逆元唯一 *)
  Theorem unique_inverse : forall a b c,
    b * a = e -> a * c = e -> b = c.
  Proof.
    intros a b c Hba Hac.
    rewrite <- (id_r b).
    rewrite <- Hac.
    rewrite <- assoc.
    rewrite Hba.
    rewrite id_l.
    reflexivity.
  Qed.
  
  (* 消去律 *)
  Theorem left_cancel : forall a b c,
    a * b = a * c -> b = c.
  Proof.
    intros a b c H0.
    rewrite <- (id_l b).
    rewrite <- (inv_l a).
    rewrite assoc.
    rewrite H0.
    rewrite <- assoc.
    rewrite inv_l.
    rewrite id_l.
    reflexivity.
  Qed.
End GroupTheorems.

(* 整数模 n 群 *)
Instance Zn_group (n : nat) (Hn : n > 0) : Group (fin n) := {
  op := fun a b => fin_add a b;
  e := fin_zero;
  inv := fun a => fin_neg a
}.
Proof.
  (* 证明群公理 *)
Admitted.

实数构造

(* 柯西序列 *)
Definition cauchy_sequence := nat -> Q.

Definition is_cauchy (s : cauchy_sequence) : Prop :=
  forall ε : Q, ε > 0 -> exists N : nat,
    forall m n : nat, m >= N -> n >= N ->
      Qabs (s m - s n) < ε.

(* 等价关系 *)
Definition cauchy_equiv (s1 s2 : cauchy_sequence) : Prop :=
  forall ε : Q, ε > 0 -> exists N : nat,
    forall n : nat, n >= N ->
      Qabs (s1 n - s2 n) < ε.

(* 实数定义为柯西序列的等价类 *)
Record R : Type := mk_real {
  seq : cauchy_sequence;
  seq_cauchy : is_cauchy seq
}.

(* 实数运算 *)
Definition Rplus (x y : R) : R.
  destruct x as [sx Hx], y as [sy Hy].
  exists (fun n => sx n + sy n).
  (* 证明和仍是柯西序列 *)
Admitted.

(* 完备性 *)
Theorem R_complete : forall s : nat -> R,
  (forall ε : Q, ε > 0 -> exists N : nat,
    forall m n : nat, m >= N -> n >= N ->
      R_dist (s m) (s n) < ε) ->
  exists L : R, forall ε : Q, ε > 0 ->
    exists N : nat, forall n : nat, n >= N ->
      R_dist (s n) L < ε.
Proof.
  (* 完备性的证明 *)
Admitted.

拓扑学基础

(* 拓扑空间 *)
Record TopologicalSpace : Type := {
  carrier : Type;
  open : (carrier -> Prop) -> Prop;
  
  open_univ : open (fun _ => True);
  open_empty : open (fun _ => False);
  open_inter : forall U V, open U -> open V -> 
    open (fun x => U x /\ V x);
  open_union : forall F : (carrier -> Prop) -> Prop,
    (forall U, F U -> open U) ->
    open (fun x => exists U, F U /\ U x)
}.

(* 连续函数 *)
Definition continuous {X Y : TopologicalSpace} 
  (f : carrier X -> carrier Y) : Prop :=
  forall V, open Y V -> open X (fun x => V (f x)).

(* 同胚 *)
Definition homeomorphism {X Y : TopologicalSpace}
  (f : carrier X -> carrier Y) (g : carrier Y -> carrier X) : Prop :=
  continuous f /\ continuous g /\
  (forall x, g (f x) = x) /\ (forall y, f (g y) = y).

(* 紧致性 *)
Definition compact (X : TopologicalSpace) : Prop :=
  forall F : (carrier X -> Prop) -> Prop,
  (forall U, F U -> open X U) ->
  (forall x, exists U, F U /\ U x) ->
  exists G : (carrier X -> Prop) -> Prop,
    (forall U, G U -> F U) /\
    (exists l : list (carrier X -> Prop),
      (forall U, In U l <-> G U) /\
      (forall x, exists U, In U l /\ U x)).
项目 4.4.1

证明康托尔定理:对任意集合 A,不存在从 A 到其幂集的满射。

4.5 程序提取

Coq 可以从证明中提取可执行程序。

提取到 OCaml

(* 定义一个验证过的排序函数 *)
Definition sort_with_proof (l : list nat) :
  { l' : list nat | sorted l' /\ permutation l l' }.
Proof.
  exists (insertion_sort l).
  apply insertion_sort_correct.
Defined.

(* 提取排序函数 *)
Definition verified_sort (l : list nat) : list nat :=
  proj1_sig (sort_with_proof l).

(* 提取指令 *)
Require Extraction.
Extraction Language OCaml.

(* 基本类型映射 *)
Extract Inductive nat => "int"
  [ "0" "(fun x -> x + 1)" ]
  "(fun zero succ n -> if n = 0 then zero () else succ (n-1))".

Extract Inductive list => "list" [ "[]" "(::)" ].

(* 提取到文件 *)
Extraction "verified_sort.ml" verified_sort.

(* 生成的 OCaml 代码示例:
let rec insert_sorted x = function
  | [] -> [x]
  | h :: t -> 
      if x <= h then x :: h :: t
      else h :: insert_sorted x t

let rec verified_sort = function
  | [] -> []
  | h :: t -> insert_sorted h (verified_sort t)
*)

提取到 Haskell

(* 切换到 Haskell *)
Extraction Language Haskell.

(* Haskell 特定的映射 *)
Extract Inductive nat => "Prelude.Integer"
  [ "0" "\\x -> x Prelude.+ 1" ]
  "\\zero succ n -> if n Prelude.== 0 then zero () else succ (n Prelude.- 1)".

Extract Inductive list => "[]" [ "[]" "(:)" ].
Extract Inductive prod => "(,)" [ "(,)" ].
Extract Inductive option => "Prelude.Maybe" [ "Prelude.Nothing" "Prelude.Just" ].

(* 提取纯函数式的二叉搜索树 *)
Extraction "BST.hs" BST insert lookup.

(* 生成的 Haskell 代码示例:
data BST a = Empty | Node a (BST a) (BST a)

insert :: Ord a => a -> BST a -> BST a
insert x Empty = Node x Empty Empty
insert x (Node y l r) =
  if x <= y
  then Node y (insert x l) r
  else Node y l (insert x r)
*)

提取优化

(* 优化提取:去除证明项 *)
Definition gcd_with_proof (a b : nat) :
  { d : nat | d > 0 /\ 
              (exists q1, a = q1 * d) /\ 
              (exists q2, b = q2 * d) /\
              (forall d', d' > 0 -> 
                (exists q1, a = q1 * d') -> 
                (exists q2, b = q2 * d') -> 
                d' <= d) }.
Proof.
  (* 欧几里得算法的验证实现 *)
Admitted.

(* 提取时只保留计算部分 *)
Definition gcd (a b : nat) : nat :=
  proj1_sig (gcd_with_proof a b).

(* 内联优化 *)
Extraction Inline gcd_with_proof.
Extraction NoInline gcd.

(* 避免提取某些定义 *)
Extract Constant mult => "( * )".
Extract Constant plus => "( + )".

(* 提取整个模块 *)
Recursive Extraction Library List.
Recursive Extraction Library Arith.

与原生代码集成

(* 外部函数接口 *)
Parameter read_file : string -> option string.
Parameter write_file : string -> string -> bool.

(* 提取到实际的文件操作 *)
Extract Constant read_file => 
  "(fun filename ->
     try 
       let ic = open_in filename in
       let n = in_channel_length ic in
       let s = Bytes.create n in
       really_input ic s 0 n;
       close_in ic;
       Some (Bytes.to_string s)
     with _ -> None)".

Extract Constant write_file =>
  "(fun filename content ->
     try
       let oc = open_out filename in
       output_string oc content;
       close_out oc;
       true
     with _ -> false)".

(* 验证的文件处理程序 *)
Definition process_file (f : string -> string) 
  (input output : string) : bool :=
  match read_file input with
  | None => false
  | Some content => write_file output (f content)
  end.

(* 使用验证过的转换函数 *)
Definition verified_transform (s : string) : string :=
  (* 某个经过验证的字符串转换 *)
  s.

Definition main : bool :=
  process_file verified_transform "input.txt" "output.txt".

Extraction "file_processor.ml" main.
项目 4.5.1

提取一个经过验证的 JSON 解析器并集成到实际应用中。

第五章:Lean 实践应用

5.1 数学形式化

Lean 在数学形式化方面有着强大的生态系统,特别是 Mathlib 库。

使用 Mathlib

import Mathlib.Data.Real.Basic
import Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic

-- 证明三角恒等式
theorem sin_squared_plus_cos_squared (x : ℝ) : 
  Real.sin x ^ 2 + Real.cos x ^ 2 = 1 := by
  simp [Real.sin_sq_add_cos_sq]

群论示例

import Mathlib.Algebra.Group.Defs

-- 定义群的性质
class MyGroup (G : Type*) extends Mul G, One G, Inv G where
  mul_assoc : ∀ a b c : G, (a * b) * c = a * (b * c)
  one_mul : ∀ a : G, 1 * a = a
  mul_one : ∀ a : G, a * 1 = a
  inv_mul_cancel : ∀ a : G, a⁻¹ * a = 1

-- 证明逆元的唯一性
theorem inv_unique {G : Type*} [MyGroup G] (a b : G) 
  (h : a * b = 1) : b = a⁻¹ := by
  have h1 : a⁻¹ * (a * b) = a⁻¹ * 1 := by rw [h]
  rw [MyGroup.mul_one] at h1
  rw [← MyGroup.mul_assoc, MyGroup.inv_mul_cancel, MyGroup.one_mul] at h1
  exact h1
习题 5.1.1

证明群中的左消去律:如果 a * b = a * c,则 b = c。

5.2 程序验证与优化

Lean 4 作为一个功能完整的编程语言,可以编写高效的验证程序。

验证排序算法

-- 定义有序列表
def Sorted : List Nat → Prop
  | [] => True
  | [_] => True
  | x :: y :: xs => x ≤ y ∧ Sorted (y :: xs)

-- 插入排序
def insertSorted (x : Nat) : List Nat → List Nat
  | [] => [x]
  | y :: ys => if x ≤ y then x :: y :: ys 
               else y :: insertSorted x ys

def insertionSort : List Nat → List Nat
  | [] => []
  | x :: xs => insertSorted x (insertionSort xs)

-- 证明插入保持有序性
theorem insertSorted_preserves_sorted (x : Nat) (l : List Nat) :
  Sorted l → Sorted (insertSorted x l) := by
  intro h
  induction l with
  | nil => simp [insertSorted, Sorted]
  | cons y ys ih =>
    simp [insertSorted]
    split_ifs with h1
    · cases ys with
      | nil => simp [Sorted, h1]
      | cons z zs => 
        simp [Sorted] at h ⊢
        exact ⟨h1, h⟩
    · cases ys with
      | nil => simp [Sorted, le_of_not_le h1]
      | cons z zs =>
        simp [Sorted] at h ⊢
        exact ⟨h.1, ih h.2⟩

使用依赖类型优化

-- 带长度信息的向量
inductive Vec (α : Type) : Nat → Type where
  | nil : Vec α 0
  | cons : α → Vec α n → Vec α (n + 1)

-- 安全的头部函数
def head {α : Type} {n : Nat} : Vec α (n + 1) → α
  | Vec.cons x _ => x

-- 向量拼接
def append {α : Type} {m n : Nat} : 
  Vec α m → Vec α n → Vec α (m + n)
  | Vec.nil, ys => ys
  | Vec.cons x xs, ys => Vec.cons x (append xs ys)
习题 5.2.1

实现向量的 map 函数,保持长度不变。

5.3 元编程与自动化

Lean 4 提供了强大的元编程能力,可以编写自定义策略和代码生成器。

自定义策略

import Lean

open Lean Meta Elab Tactic

-- 定义一个简单的策略
def myTrivial : TacticM Unit := do
  let goal ← getMainGoal
  let goalType ← goal.getType
  
  -- 尝试 rfl
  try
    let expr ← mkAppM ``Eq.refl #[← mkFreshExprMVar goalType]
    goal.assign expr
    return
  catch _ => pure ()
  
  -- 尝试 trivial
  evalTactic (← `(tactic| trivial))

-- 注册策略
elab "my_trivial" : tactic => myTrivial

-- 使用示例
example : 2 + 2 = 4 := by my_trivial

宏和语法扩展

-- 定义新的语法
syntax "prove_by_cases" term "with" cases:term,* : tactic

macro_rules
  | `(tactic| prove_by_cases $e with $[$cases],*) => 
    `(tactic| cases $e <;> [$[$cases],*])

-- 使用新语法
example (p q : Prop) : p ∨ q → q ∨ p := by
  intro h
  prove_by_cases h with
    (right; assumption),
    (left; assumption)
习题 5.3.1

编写一个策略,自动处理简单的算术等式。

5.4 交互式定理证明

Lean 的交互式证明环境提供了丰富的工具支持。

结构化证明

-- 使用 have 和 show 的结构化证明
theorem sqrt_two_irrational : ¬ ∃ (p q : ℕ), q ≠ 0 ∧ p * p = 2 * q * q := by
  intro ⟨p, q, hq, h_eq⟩
  
  -- 假设 p 和 q 互质
  have h_coprime : Nat.gcd p q = 1 := by sorry  -- 简化处理
  
  -- p² 是偶数,所以 p 是偶数
  have p_even : Even p := by
    have : Even (p * p) := by
      use q * q
      rw [h_eq]
      ring
    exact even_of_even_sq this
  
  -- 设 p = 2k
  obtain ⟨k, hk⟩ := p_even
  
  -- 代入原式得到 q² 也是偶数
  have q_even : Even q := by
    have : 2 * k * (2 * k) = 2 * q * q := by
      rw [← hk] at h_eq
      exact h_eq
    have : 2 * (2 * k * k) = 2 * q * q := by ring_nf at this ⊢; exact this
    have : 2 * k * k = q * q := by linarith
    have : Even (q * q) := by use k * k; exact this.symm
    exact even_of_even_sq this
  
  -- 但这与互质矛盾
  sorry  -- 完整证明需要更多细节

计算证明(calc)

-- 使用 calc 进行链式推理
example (a b c d : ℝ) (h1 : a = b) (h2 : b < c) (h3 : c ≤ d) : 
  a < d := by
  calc a = b := h1
       _ < c := h2
       _ ≤ d := h3
习题 5.4.1

使用结构化证明方法证明:如果 n 是偶数,那么 n² 也是偶数。

5.5 Lean 项目实践

本节介绍如何组织和管理 Lean 项目。

项目结构

# 创建新项目
lake new my_project
cd my_project

# 项目结构
my_project/
├── MyProject.lean       # 主文件
├── MyProject/
│   ├── Basic.lean      # 基础定义
│   ├── Theorems.lean   # 定理证明
│   └── Examples.lean   # 示例
├── lakefile.lean       # 构建配置
└── lean-toolchain      # Lean 版本

使用 Lake 构建系统

-- lakefile.lean
import Lake
open Lake DSL

package «my_project» where
  -- 添加依赖
  dependencies := #[
    {
      name := `mathlib
      src := Source.git 
        "https://github.com/leanprover-community/mathlib4.git" 
        "main"
    }
  ]

@[default_target]
lean_lib «MyProject» where
  -- 库配置
  roots := #[`MyProject]

文档和测试

/-!
# My Project

这是项目文档的主页。

## 主要内容

- 基础定义
- 核心定理
- 应用示例
-/

-- 文档字符串
/-- 计算列表的长度 -/
def myLength {α : Type} : List α → Nat
  | [] => 0
  | _ :: xs => 1 + myLength xs

-- 单元测试
#eval myLength [1, 2, 3]  -- 期望输出: 3

-- 属性测试
theorem myLength_correct {α : Type} (l : List α) :
  myLength l = l.length := by
  induction l with
  | nil => rfl
  | cons x xs ih => simp [myLength, List.length, ih]
习题 5.5.1

创建一个简单的 Lean 包,实现并验证一个栈数据结构。

附录

A. Coq 标准库参考

常用模块

  • Arith - 自然数算术
  • List - 列表操作
  • Bool - 布尔运算
  • Logic - 逻辑连接词
  • Relations - 关系理论
  • Sorting - 排序算法
  • Wellfounded - 良基关系

重要策略速查

(* 基本策略 *)
reflexivity    (* 证明反身性 *)
symmetry       (* 交换等式两边 *)
transitivity   (* 传递性 *)
assumption     (* 使用假设 *)
exact H        (* 精确应用 *)

(* 引入和应用 *)
intros         (* 引入全称量词 *)
apply H        (* 应用假设或定理 *)
eapply H       (* 延迟实例化 *)

(* 分解 *)
destruct       (* 分情况讨论 *)
induction      (* 归纳法 *)
inversion      (* 反演 *)

(* 重写 *)
rewrite H      (* 使用等式重写 *)
rewrite <- H   (* 反向重写 *)
subst          (* 替换变量 *)

(* 简化 *)
simpl          (* 简化表达式 *)
unfold f       (* 展开定义 *)
fold f         (* 折叠定义 *)

(* 自动化 *)
auto           (* 自动证明 *)
eauto          (* 扩展自动证明 *)
lia            (* 线性算术 *)
ring           (* 环运算 *)
field          (* 域运算 *)

B. Coq 常见错误与调试

类型错误

错误:The term "x" has type "nat" while it is expected to have type "bool"

解决:检查类型是否匹配,可能需要类型转换函数。

未完成的证明

错误:Error: Attempt to save an incomplete proof

解决:使用 Admitted 暂时承认,或完成所有子目标。

策略失败

错误:Error: No applicable tactic

解决:检查当前目标,可能需要先做一些准备工作。

C. Lean 标准库参考

常用模块

  • Init - 基础定义和符号
  • Std - 标准库扩展
  • Mathlib - 数学库
  • Mathlib.Data.Nat - 自然数
  • Mathlib.Data.List - 列表操作
  • Mathlib.Logic.Basic - 基础逻辑
  • Mathlib.Tactic - 策略库
  • Mathlib.Algebra - 代数结构
  • Mathlib.Analysis - 分析学
  • Mathlib.Topology - 拓扑学

重要策略速查

-- 基本策略
rfl           -- 证明反身性
symm          -- 交换等式两边
trans         -- 传递性
assumption    -- 使用假设
exact h       -- 精确应用

-- 引入和应用
intro         -- 引入假设
intros        -- 引入多个假设
apply h       -- 应用假设或定理
refine        -- 精细化目标

-- 分解
cases         -- 分情况讨论
induction     -- 归纳法
match         -- 模式匹配
obtain        -- 解构存在量词

-- 重写
rw [h]        -- 使用等式重写
simp          -- 简化器
simp only     -- 受限简化
conv          -- 转换模式

-- 简化
dsimp         -- 定义简化
unfold f      -- 展开定义
change        -- 改变目标形式

-- 自动化
simp          -- 简化器
ring          -- 环策略
linarith      -- 线性算术
norm_num      -- 数值计算
omega         -- Presburger 算术
aesop         -- 自动证明搜索

-- 数学专用
field_simp    -- 域简化
ring_nf       -- 环标准形式
group         -- 群论策略
abel          -- 阿贝尔群策略

类型类实例

-- 常用类型类
instance : Add Nat := ⟨Nat.add⟩
instance : Mul Nat := ⟨Nat.mul⟩
instance : OfNat Nat n := ⟨n⟩

-- 代数结构
instance : Monoid α where
  mul := (· * ·)
  one := 1
  mul_assoc := mul_assoc
  one_mul := one_mul
  mul_one := mul_one

-- 自定义实例
instance : ToString MyType where
  toString x := s!"MyType: {x.field}"

元编程基础

-- 自定义策略
syntax "my_tactic" : tactic
macro_rules
  | `(tactic| my_tactic) => `(tactic| simp; rfl)

-- 宏定义
macro "prove_by" t:tactic : tactic =>
  `(tactic| first | $t | simp | rfl)

-- 元变量操作
open Lean Meta in
def myTactic : TacticM Unit := do
  let goal ← getMainGoal
  let type ← goal.getType
  trace[myTactic] "Goal type: {type}"

D. Lean 常见错误与调试

类型错误

错误:type mismatch

-- 错误示例
def f (n : Nat) : Bool := n  -- 错误:期望 Bool,得到 Nat

-- 修正
def f (n : Nat) : Bool := n > 0

类型推断失败

错误:failed to synthesize instance

-- 错误示例
#check (2 : ℝ) + (3 : ℕ)  -- 错误:无法推断加法实例

-- 修正
#check (2 : ℝ) + (3 : ℝ)  -- 显式类型标注
#check (2 : ℝ) + ↑3       -- 使用强制转换

策略失败

错误:tactic failed

-- 调试技巧
theorem my_theorem : P := by
  trace_state  -- 显示当前目标
  sorry        -- 暂时跳过

-- 使用 try 和 first
theorem my_theorem : P := by
  try simp     -- 尝试简化
  first        -- 尝试多个策略
  | rfl
  | assumption
  | sorry

终止性检查

错误:failed to prove termination

-- 错误示例
def badFib : Nat → Nat
  | 0 => 0
  | 1 => 1
  | n => badFib (n - 1) + badFib (n - 2)  -- 错误:n-2 可能不减小

-- 修正1:使用 termination_by
def fib : Nat → Nat
  | 0 => 0
  | 1 => 1
  | n + 2 => fib n + fib (n + 1)

-- 修正2:显式终止证明
def fib' (n : Nat) : Nat :=
  match n with
  | 0 => 0
  | 1 => 1
  | n + 2 => fib' n + fib' (n + 1)
termination_by n

Universe 级别错误

错误:universe level mismatch

-- 错误示例
def bad : Type → Type := fun α => List α  -- 错误:universe 不匹配

-- 修正
def good : Type u → Type u := fun α => List α
-- 或
def good' (α : Type u) : Type u := List α

调试工具

-- 跟踪选项
set_option trace.Meta.synthInstance true  -- 跟踪实例合成
set_option trace.simp true               -- 跟踪简化器
set_option pp.all true                   -- 显示所有隐式参数

-- 检查表达式
#check @List.map        -- 查看完整类型
#print List.map         -- 查看定义
#reduce 2 + 3          -- 计算表达式

-- 目标检查
example : P := by
  trace_target           -- 显示目标
  trace_state           -- 显示完整状态
  sorry

-- 性能分析
set_option profiler true
#eval expensive_computation

包管理问题

Lake 构建错误处理:

# 清理缓存
lake clean

# 更新依赖
lake update

# 详细输出
lake build -v

# 检查 lean-toolchain
cat lean-toolchain

E. Ltac:Coq 的策略语言

Ltac (Language of Tactics) 是 Coq 的策略编程语言,允许用户编写自定义策略、组合现有策略,并实现证明自动化。

为什么需要 Ltac?

Ltac 存在的核心原因是证明的重复性模式需要自动化:

  • 手动证明的痛点:很多证明包含大量重复的步骤模式
  • 领域特定性:不同领域的证明有不同的模式,需要定制化
  • 可组合性:基本策略需要灵活组合来解决复杂问题

本质上,Ltac 就是一个模式匹配的状态机,它的设计目标是:

  • 对证明状态(goals 和 hypotheses)进行模式匹配
  • 根据匹配结果选择和组合策略
  • 提供回溯机制处理失败情况

什么是 Ltac?

Ltac 是一种领域特定语言,专门用于:

  • 组合和编排基本策略
  • 编写可重用的证明模式
  • 实现证明自动化
  • 处理证明中的重复性工作

基础语法

定义简单的 Ltac 策略
(* 定义一个简单的策略 *)
Ltac my_simpl := simpl; reflexivity.

(* 使用自定义策略 *)
Theorem test : 2 + 2 = 4.
Proof.
  my_simpl.  (* 相当于执行 simpl; reflexivity *)
Qed.

(* 带参数的策略 *)
Ltac apply_n_times tac n :=
  match n with
  | O => idtac  (* 什么都不做 *)
  | S ?n' => tac; apply_n_times tac n'
  end.

策略组合器

顺序组合 (;)
(* tac1; tac2 - 先执行 tac1,然后对所有子目标执行 tac2 *)
Ltac intro_and_simpl := intros; simpl.

(* 选择性组合 *)
Ltac solve_simple :=
  simpl;
  first [ reflexivity | assumption | trivial ].
并行组合 (||)
(* tac1 || tac2 - 尝试 tac1,如果失败则尝试 tac2 *)
Ltac auto_solve :=
  reflexivity || assumption || auto.

(* try - 尝试执行,失败也不报错 *)
Ltac safe_rewrite H :=
  try rewrite H.

模式匹配

对目标进行模式匹配
Ltac break_and :=
  match goal with
  | [ |- ?A /\ ?B ] => split
  end.

(* 更复杂的模式匹配 *)
Ltac find_eq_and_rewrite :=
  match goal with
  | [ H : ?x = ?y |- context[?x] ] => rewrite H
  | [ H : ?x = ?y |- context[?y] ] => rewrite <- H
  end.
对假设进行模式匹配
Ltac contradict :=
  match goal with
  | [ H : False |- _ ] => contradiction
  | [ H : ?P, H' : ~ ?P |- _ ] => contradiction
  end.

(* 查找并使用等式 *)
Ltac use_eq :=
  match goal with
  | [ H : _ = _ |- _ ] => rewrite H || rewrite <- H
  end.

实用 Ltac 策略示例

自动化简单证明
(* 解决简单的算术等式 *)
Ltac crush_arith :=
  intros;
  repeat (simpl in *; 
          try ring;
          try lia;
          auto).

(* 自动分解合取和存在量词 *)
Ltac decompose_ex :=
  repeat match goal with
  | [ H : exists x, _ |- _ ] => destruct H
  | [ H : _ /\ _ |- _ ] => destruct H
  end.
调试辅助策略
(* 打印当前目标状态 *)
Ltac print_goal :=
  match goal with
  | [ |- ?G ] => idtac "Current goal:" G
  end.

(* 标记证明步骤 *)
Ltac step msg :=
  idtac "Step:" msg;
  print_goal.

高级特性

递归策略
(* 递归地应用简化 *)
Ltac simpl_all :=
  simpl;
  repeat match goal with
  | [ H : _ |- _ ] => simpl in H
  end.

(* 深度优先搜索证明 *)
Ltac depth_first_proof n :=
  match n with
  | O => fail
  | S ?n' =>
    first [ reflexivity
          | assumption
          | (progress simpl; depth_first_proof n')
          | (progress auto; depth_first_proof n') ]
  end.
上下文操作
(* 清理无用假设 *)
Ltac cleanup :=
  repeat match goal with
  | [ H : ?X = ?X |- _ ] => clear H
  | [ H : True |- _ ] => clear H
  end.

(* 生成新假设 *)
Ltac remember_all :=
  repeat match goal with
  | [ |- context[?f ?x] ] =>
    let y := fresh "y" in
    remember (f x) as y
  end.

Ltac2:下一代策略语言

Coq 正在开发 Ltac2,这是 Ltac 的继任者,提供:

  • 静态类型系统
  • 更好的性能
  • 更清晰的语义
  • 与 OCaml 更好的互操作性
Ltac2 示例
From Ltac2 Require Import Ltac2.

(* Ltac2 策略定义 *)
Ltac2 my_auto () :=
  first [ reflexivity ()
        | assumption ()
        | auto () ].

(* 使用 Ltac2 策略 *)
Goal forall x : nat, x = x.
Proof.
  ltac2:(my_auto ()).
Qed.

最佳实践

  1. 命名规范:使用描述性名称,如 solve_by_induction 而非 tac1
  2. 错误处理:使用 tryfirst 来处理可能失败的策略
  3. 模块化:将复杂策略分解为小的、可重用的组件
  4. 文档:为复杂的 Ltac 策略添加注释说明其用途
  5. 调试:使用 idtacfail 来调试策略
练习:编写 Ltac 策略

编写一个 Ltac 策略 solve_bool_eq,自动解决关于布尔值的简单等式。

高级自动化技术

智能假设管理
(* 自动应用相关假设 *)
Ltac smart_apply :=
  match goal with
  | [ H : forall x, ?P x -> ?Q x, H' : ?P ?a |- ?Q ?a ] => 
    apply (H a H')
  | [ H : ?P -> ?Q, H' : ?P |- ?Q ] => 
    apply (H H')
  | [ H : forall x, ?P x |- ?P ?a ] => 
    apply H
  end.

(* 传递性链式推理 *)
Ltac chain_trans :=
  match goal with
  | [ H1 : ?a = ?b, H2 : ?b = ?c |- ?a = ?c ] =>
    transitivity b; [exact H1 | exact H2]
  | [ H1 : ?a <= ?b, H2 : ?b <= ?c |- ?a <= ?c ] =>
    transitivity b; [exact H1 | exact H2]
  end.

(* 自动前向推理 *)
Ltac forward_reasoning :=
  repeat match goal with
  | [ H : ?P -> ?Q, H' : ?P |- _ ] =>
    let H_new := fresh "H" in
    assert (H_new : Q) by (apply H; exact H')
  end.
归纳证明自动化
(* 自动归纳策略 *)
Ltac auto_induction x :=
  induction x;
  intros;
  simpl in *;
  try reflexivity;
  try (rewrite IHx; reflexivity);
  try (rewrite <- IHx; reflexivity);
  auto.

(* 列表相关的自动化 *)
Ltac list_solver :=
  intros;
  repeat match goal with
  | [ |- context[?l ++ nil] ] => rewrite app_nil_r
  | [ |- context[nil ++ ?l] ] => rewrite app_nil_l
  | [ |- context[(?l1 ++ ?l2) ++ ?l3] ] => rewrite app_assoc
  | [ H : ?l = nil |- _ ] => destruct l; [clear H | discriminate H]
  | [ H : nil = ?l |- _ ] => destruct l; [clear H | discriminate H]
  | [ H : ?h :: ?t = nil |- _ ] => discriminate H
  | [ H : nil = ?h :: ?t |- _ ] => discriminate H
  end;
  auto.

(* 自然数归纳的高级策略 *)
Ltac nat_ind_strong P :=
  intro n;
  pattern n;
  apply (nat_ind P);
  [clear n | intro n'; intro IHn'].
反证法自动化
(* 自动反证法 *)
Ltac by_contradiction :=
  match goal with
  | [ |- ?P ] =>
    destruct (classic P) as [H | H];
    [exact H | exfalso]
  end.

(* 寻找矛盾 *)
Ltac find_contradiction :=
  match goal with
  | [ H : ?P, H' : ~ ?P |- _ ] => contradiction
  | [ H : ?x <> ?x |- _ ] => contradiction
  | [ H : ?x = ?y, H' : ?x <> ?y |- _ ] => contradiction
  | [ H : ?x < ?x |- _ ] => apply lt_irrefl in H; contradiction
  | [ H : S ?n <= ?n |- _ ] => apply le_S_n in H; apply le_Sn_n in H; contradiction
  end.
定制化证明搜索
(* 带深度限制的证明搜索 *)
Ltac bounded_search depth :=
  match depth with
  | O => fail "Search depth exceeded"
  | S ?d =>
    first [ assumption
          | reflexivity
          | (progress simpl; bounded_search d)
          | (progress auto; bounded_search d)
          | (left; bounded_search d)
          | (right; bounded_search d)
          | (split; bounded_search d)
          | (intro; bounded_search d) ]
  end.

(* 启发式证明搜索 *)
Ltac heuristic_prove :=
  (* 先尝试简单策略 *)
  try reflexivity;
  try assumption;
  try discriminate;
  (* 然后尝试分解 *)
  repeat match goal with
  | [ |- _ /\ _ ] => split
  | [ H : _ /\ _ |- _ ] => destruct H
  | [ H : _ \/ _ |- _ ] => destruct H
  | [ |- exists _, _ ] => eexists
  end;
  (* 最后尝试自动化 *)
  auto.
领域特定的自动化
(* 集合论证明自动化 *)
Ltac set_solver :=
  intros;
  unfold subset, element_of, empty_set in *;
  firstorder.

(* 排列组合证明自动化 *)
Ltac perm_solver :=
  intros;
  repeat match goal with
  | [ |- Permutation ?l ?l ] => reflexivity
  | [ H : Permutation ?l1 ?l2 |- Permutation ?l2 ?l1 ] => 
    apply Permutation_sym; exact H
  | [ H1 : Permutation ?l1 ?l2, H2 : Permutation ?l2 ?l3 |- Permutation ?l1 ?l3 ] =>
    eapply Permutation_trans; [exact H1 | exact H2]
  end.

(* 等价关系证明自动化 *)
Ltac equiv_solver :=
  intros;
  repeat match goal with
  | [ |- ?R ?x ?x ] => apply refl_equiv
  | [ H : ?R ?x ?y |- ?R ?y ?x ] => apply sym_equiv; exact H
  | [ H1 : ?R ?x ?y, H2 : ?R ?y ?z |- ?R ?x ?z ] =>
    eapply trans_equiv; [exact H1 | exact H2]
  end.

性能优化技巧

(* 避免重复计算 *)
Ltac cache_computation term :=
  let v := fresh "cached" in
  remember term as v.

(* 惰性匹配 - 只在需要时匹配 *)
Ltac lazy_match :=
  lazymatch goal with
  | [ |- ?P /\ ?Q ] => split
  | [ |- ?P ] => fail "Not a conjunction"
  end.

(* 多线程策略(概念性) *)
Ltac parallel_try tac1 tac2 :=
  first [progress tac1 | progress tac2].

(* 记忆化策略结果 *)
Ltac memoized_auto := 
  let cache := fresh "cache" in
  assert (cache : True) by constructor;
  auto;

调试和错误处理

(* 详细的错误信息 *)
Ltac fail_with_context msg :=
  match goal with
  | [ |- ?G ] => fail "Error:" msg "in goal:" G
  end.

(* 断言和前置条件检查 *)
Ltac assert_precondition P :=
  assert P by (auto || fail "Precondition failed").

(* 策略执行跟踪 *)
Ltac trace_exec tac name :=
  idtac "Entering" name;
  let result := tac in
  idtac "Exiting" name;
  result.

(* 超时处理 *)
Ltac with_timeout n tac :=
  timeout n tac.

与其他 Coq 特性的集成

(* 与类型类集成 *)
Ltac typeclass_solver :=
  typeclasses eauto.

(* 与 Program 集成 *)
Ltac program_simpl :=
  program_simpl;
  try solve [program_solve_wf].

(* 与 setoid 重写集成 *)
Ltac setoid_rewrite_all :=
  repeat match goal with
  | [ H : ?R ?x ?y |- context[?x] ] => setoid_rewrite H
  end.

(* 与 ssreflect 集成 *)
Ltac ssr_auto :=
  by []; done.
高级练习:编写归纳证明自动化

编写一个 Ltac 策略,自动证明关于列表长度的简单性质。

实战中的 Ltac 最佳实践

大型项目中的 Ltac 组织
(* 创建模块化的策略库 *)
Module MyTactics.
  
  (* 基础策略 *)
  Ltac basic_cleanup :=
    intros;
    simpl in *;
    unfold not in *;
    repeat match goal with
    | [ H : ?X = ?X |- _ ] => clear H
    | [ H : True |- _ ] => clear H
    | [ H : ?X -> ?X |- _ ] => clear H
    end.
  
  (* 中级策略 *)
  Ltac solve_by_inverting :=
    basic_cleanup;
    repeat match goal with
    | [ H : ?f _ = ?f _ |- _ ] => injection H; clear H; intros; subst
    | [ H : Some _ = Some _ |- _ ] => injection H; clear H; intros; subst
    | [ H : Some _ = None |- _ ] => discriminate H
    | [ H : None = Some _ |- _ ] => discriminate H
    end;
    auto.
  
  (* 高级策略 *)
  Ltac full_auto :=
    basic_cleanup;
    solve_by_inverting;
    repeat (progress (auto; firstorder)).
    
End MyTactics.

Import MyTactics.
领域特定语言 (DSL) 的策略
(* 为编译器验证设计的策略 *)
Ltac eval_step :=
  match goal with
  | [ |- context[eval_expr (EConst ?n)] ] => 
    unfold eval_expr; simpl
  | [ |- context[eval_expr (EPlus ?e1 ?e2)] ] =>
    unfold eval_expr; simpl;
    rewrite <- eval_expr_sound
  | [ |- context[eval_expr (EVar ?x)] ] =>
    unfold eval_expr; simpl;
    rewrite lookup_correct
  end.

(* 类型系统的自动化 *)
Ltac type_check :=
  repeat match goal with
  | [ |- has_type _ (TConst _) TInt ] => 
    constructor
  | [ |- has_type ?env (TVar ?x) ?t ] => 
    apply T_Var; apply lookup_env
  | [ |- has_type ?env (TApp ?f ?x) ?t ] =>
    eapply T_App; [type_check | type_check]
  end.
复杂证明的组织
(* 使用子目标标记 *)
Ltac prove_with_labels :=
  match goal with
  | [ |- ?P /\ ?Q ] => 
    split; 
    [ idtac "Proving" P; prove_with_labels
    | idtac "Proving" Q; prove_with_labels ]
  | [ |- forall x, ?P x ] =>
    let x' := fresh x in
    intro x';
    idtac "Proving for" x';
    prove_with_labels
  | _ => auto
  end.

(* 结构化证明 *)
Ltac structured_proof :=
  (* 阶段1: 初始化 *)
  intros; simpl in *;
  (* 阶段2: 分解复杂结构 *)
  repeat match goal with
  | [ H : exists x, _ |- _ ] => destruct H
  | [ H : _ /\ _ |- _ ] => destruct H
  | [ |- _ /\ _ ] => split
  end;
  (* 阶段3: 应用域特定知识 *)
  repeat match goal with
  | [ H : ?P -> ?Q, H' : ?P |- _ ] => 
    apply H in H'
  end;
  (* 阶段4: 终结 *)
  auto.

性能调优和经验教训

  1. 避免回溯:使用 lazymatch 代替 match 来避免不必要的回溯
  2. 早期失败:在不可能成功的情况下尽早使用 fail
  3. 缓存结果:使用 remember 来避免重复计算
  4. 限制搜索空间:使用 onlyusing 来限制自动化策略的搜索范围
  5. 渐进式开发:先编写简单版本,然后逐步优化
调试复杂 Ltac 程序
(* 使用 Info 命令 *)
Goal forall n m : nat, n + m = m + n.
Proof.
  intros.
  Info 2 auto.  (* 查看 auto 执行的详细步骤 *)
  
  (* 使用 Show Ltac Profile *)
  Set Ltac Profiling.
  (* 执行一些策略 *)
  Show Ltac Profile.
  
  (* 使用 debug 模式 *)
  Set Ltac Debug.
  auto.
  Unset Ltac Debug.
Abort.

Ltac 的可编程性和替代方案

Ltac 的 AST 和外部访问

Ltac 确实有内部 AST 表示,但:

  • 有限的外部访问:Ltac 的 AST 不像 MetaCoq 那样完全暴露给外部
  • Ltac2 的改进:新的 Ltac2 提供了更好的程序化接口
  • ML 插件:可以通过 OCaml 插件访问更底层的证明状态
OCaml 插件示例
(* OCaml 插件直接操作证明状态 *)
let my_tactic : unit Proofview.tactic =
  Proofview.Goal.enter (fun gl ->
    let env = Proofview.Goal.env gl in
    let sigma = Proofview.Goal.sigma gl in
    let concl = Proofview.Goal.concl gl in
    (* 直接操作证明状态 *)
    ...
  )
不使用 Ltac 的替代方案
1. SerAPI / coq-lsp
# Python 示例使用 SerAPI
from serapi import SerAPI

coq = SerAPI()
coq.add("Theorem test : 2 + 2 = 4.")
state = coq.query_goals()
# 程序化地分析状态并发送策略
coq.execute("simpl.")
coq.execute("reflexivity.")
2. MetaCoq 直接构造证明
From MetaCoq.Template Require Import All.

(* 直接构造证明项 *)
Definition my_proof : 2 + 2 = 4 :=
  eq_refl 4.

(* 或者程序化生成证明 *)
MetaCoq Quote Definition q_proof := my_proof.
3. Elpi (λProlog 策略语言)
% Elpi 策略示例
solve (goal _ _ {{ lp:A = lp:B }} as G) GL :-
  % 直接操作证明状态
  coq.unify-eq A B ok,
  refine {{ eq_refl lp:A }} G GL.
4. 直接构造证明项
(* 完全跳过策略,直接写证明项 *)
Definition my_theorem : forall n : nat, n + 0 = n :=
  fix f n := match n with
  | 0 => eq_refl 0
  | S n' => f_equal S (f n')
  end.
为什么 Ltac 仍然流行?
  1. 集成性:Ltac 深度集成在 Coq 中,无需额外工具
  2. 表达力:对证明状态的模式匹配很自然
  3. 生态系统:大量现有代码使用 Ltac
  4. 学习曲线:比 OCaml 插件或 MetaCoq 更容易上手

未来展望

Ltac 虽然强大,但也有一些局限性。Coq 社区正在开发新的解决方案:

  • Ltac2:提供静态类型和更好的性能
  • Elpi:基于 λProlog 的策略语言
  • MetaCoq:元编程框架
  • ML 策略:使用 OCaml 编写高性能策略

掌握 Ltac 是成为 Coq 高手的必经之路。通过编写自定义策略,您可以大大提高证明效率,并为特定领域构建强大的自动化工具。

Ltac2:下一代策略语言

设计动机

Ltac1 的主要问题:

  • 动态类型:运行时才发现类型错误
  • 性能问题:解释执行,回溯开销大
  • 语义模糊:求值顺序不明确
  • 调试困难:错误信息不友好
Ltac2 的核心特性
1. 静态类型系统
From Ltac2 Require Import Ltac2.

(* 类型明确的策略定义 *)
Ltac2 my_tactic (n : int) (tac : unit -> unit) : unit :=
  match Int.equal n 0 with
  | true => ()
  | false => tac (); my_tactic (Int.sub n 1) tac
  end.

(* 多态策略 *)
Ltac2 map_hyps (f : ident -> constr -> unit) : unit :=
  List.iter (fun h => 
    let typ := Constr.type (Control.hyp h) in
    f h typ
  ) (Control.hyps ()).
2. 异常处理
Ltac2 Type exn ::= [ MyError (string) ].

Ltac2 safe_assumption () :=
  match Control.case (fun () => assumption) with
  | Val _ => Message.print (Message.of_string "Assumption succeeded")
  | Err e => Control.throw (MyError "No matching assumption")
  end.
3. 模块系统
Ltac2 mutable debug : bool := false.

Module MyTactics.
  Ltac2 trace (msg : string) :=
    if debug then Message.print (Message.of_string msg)
    else ().
    
  Ltac2 smart_intro () :=
    trace "Introducing...";
    intro.
End MyTactics.
4. 更好的控制流
(* 明确的求值控制 *)
Ltac2 lazy_or tac1 tac2 :=
  Control.plus (fun () => tac1 ()) (fun _ => tac2 ()).

(* 高效的循环 *)
Ltac2 repeat_n (n : int) (tac : unit -> unit) :=
  let rec aux i :=
    if Int.equal i 0 then ()
    else (tac (); aux (Int.sub i 1))
  in aux n.
Ltac2 实战示例
Ltac2 crush_arith () :=
  (* 类型安全的模式匹配 *)
  repeat (
    first [
      reflexivity
    | progress (Std.intros)
    | progress (Std.simpl)
    | match! goal with
      | [ h : ?x = ?y |- _ ] => Std.rewrite h
      | [ |- context [?n + 0] ] => 
        Pattern.rewrite (fun () => open_constr:(_ + 0)) 
                        (fun () => open_constr:(_))
                        (fun () => plus_n_O)
      end
    ]
  ).

(* 使用 *)
Goal forall n m : nat, n + 0 + m = n + m.
Proof.
  ltac2:(crush_arith ()).
Qed.
Ltac2 vs Ltac1 对比
特性 Ltac1 Ltac2
类型系统 动态类型 静态类型
性能 解释执行 编译执行
错误处理 基本 完善的异常系统
模块化 有限 完整模块系统
学习曲线 简单 中等
simpl in *; try reflexivity; try discriminate; try contradiction. (* 测试 *) Theorem test_bool_eq : forall b1 b2 : bool, b1 && b2 = true -> b1 = true /\ b2 = true. Proof. solve_bool_eq. Qed.

F. MetaCoq:元编程框架

MetaCoq 是 Coq 的元编程框架,允许在 Coq 内部操作和生成 Coq 项。它提供了对 Coq 的内部表示的完全访问。

核心概念

MetaCoq 提供了:

  • 具体化(Reification):将 Coq 项转换为 AST
  • 反具体化(Reflection):将 AST 转换回 Coq 项
  • 类型检查器:在 Coq 内验证项的类型
  • 求值器:在 Coq 内求值项

基本使用

1. Quote:获取 AST
From MetaCoq.Template Require Import All.

Definition foo := 42.

(* 获取 foo 的 AST *)
MetaCoq Quote Definition foo_ast := foo.
(* foo_ast = tConst "Top.foo" [] *)

(* 获取更复杂的项 *)
MetaCoq Quote Definition plus_ast := (fun x y : nat => x + y).
Print plus_ast.
(* 显示完整的 AST 结构 *)
2. Unquote:从 AST 生成项
(* 程序化构造 AST *)
Definition my_const_ast := 
  tConstruct {| inductive_mind := "Coq.Init.Datatypes.nat"; 
                inductive_ind := 0 |} 1 [].  (* S 构造子 *)

(* 转换回 Coq 项 *)
MetaCoq Unquote Definition my_num := 
  (tApp my_const_ast [tConstruct ... 0 []]).  (* S O *)
(* my_num = 1 *)
3. 程序化证明生成
From MetaCoq.Template Require Import All.
Import MCMonadNotation.

(* 自动生成等式证明 *)
Definition make_eq_proof (n : nat) : TemplateMonad unit :=
  let rhs := tApp (tConst "Nat.add" []) 
                  [tConst "n" []; tConstruct ... 0 []] in
  let eq_type := tApp (tConst "eq" []) [tInd ...; tConst "n" []; rhs] in
  proof <- tmLemma ("n_plus_0_eq_" ++ string_of_nat n) eq_type ;;
  tmDefinition ("proof_" ++ string_of_nat n) proof.

(* 运行元程序 *)
MetaCoq Run (make_eq_proof 5).

高级应用

1. 自动派生实例
(* 自动生成 Decidable 实例 *)
MetaCoq Derive Decidable for nat.
MetaCoq Derive Decidable for list.

(* 自动生成归纳原理 *)
MetaCoq Derive Induction for tree.
2. 程序变换
(* 定义优化变换 *)
Definition optimize_plus (t : term) : term :=
  match t with
  | tApp (tConst "Nat.add" _) [x; tConstruct _ 0 []] => x  (* x + 0 => x *)
  | tApp (tConst "Nat.add" _) [tConstruct _ 0 []; y] => y  (* 0 + y => y *)
  | _ => t
  end.

(* 应用到具体函数 *)
MetaCoq Quote Definition original := (fun n => n + 0 + 0).
Definition optimized := optimize_plus original.
MetaCoq Unquote Definition better_fun := optimized.
3. 验证编译器
(* 简单的编译器示例 *)
Inductive expr :=
  | EConst (n : nat)
  | EPlus (e1 e2 : expr).

Fixpoint compile (e : expr) : term :=
  match e with
  | EConst n => tConstruct ... (* 编码自然数 *)
  | EPlus e1 e2 => 
    tApp (tConst "Nat.add" []) [compile e1; compile e2]
  end.

(* 证明编译器正确性 *)
Lemma compile_correct : forall e,
  eval (unquote (compile e)) = eval_expr e.

MetaCoq 的主要组件

AST 类型
(* MetaCoq 的核心 AST 类型 *)
Inductive term :=
  | tRel (n : nat)                          (* de Bruijn 索引 *)
  | tVar (id : ident)                       (* 变量 *)
  | tEvar (ev : nat) (args : list term)    (* 存在变量 *)
  | tSort (s : sort)                        (* 类型宇宙 *)
  | tProd (na : name) (ty : term) (body : term)  (* 依赖乘积 *)
  | tLambda (na : name) (ty : term) (body : term) (* 函数 *)
  | tLetIn (na : name) (def : term) (def_ty : term) (body : term)
  | tApp (f : term) (args : list term)     (* 应用 *)
  | tConst (c : kername) (u : list Level.t) (* 常量 *)
  | tInd (ind : inductive) (u : list Level.t) (* 归纳类型 *)
  | tConstruct (ind : inductive) (idx : nat) (u : list Level.t)
  | tCase (ind_nbparams_relevance : inductive * nat * relevance)
          (type_info : term)
          (discr : term)
          (branches : list (nat * term))
  | tProj (proj : projection) (t : term)
  | tFix (mfix : mfixpoint term) (idx : nat)
  | tCoFix (mfix : mfixpoint term) (idx : nat)
  | tInt (i : PrimInt63.int)
  | tFloat (f : PrimFloat.float).
Template Monad
(* MetaCoq 的 Template Monad 操作 *)
Inductive TemplateMonad : Type -> Type :=
  | tmReturn {A} : A -> TemplateMonad A
  | tmBind {A B} : TemplateMonad A -> (A -> TemplateMonad B) -> 
                   TemplateMonad B
  | tmPrint : term -> TemplateMonad unit
  | tmMsg : string -> TemplateMonad unit
  | tmFail {A} : string -> TemplateMonad A
  | tmEval : reductionStrategy -> term -> TemplateMonad term
  | tmDefinition : ident -> option term -> term -> TemplateMonad unit
  | tmAxiom : ident -> term -> TemplateMonad unit
  | tmLemma : ident -> term -> TemplateMonad term
  | tmFreshName : ident -> TemplateMonad ident
  | tmQuote : forall {A}, A -> TemplateMonad term
  | tmUnquote : term -> TemplateMonad typed_term
  | tmUnquoteTyped : forall A, term -> TemplateMonad A
  | tmExistingInstance : ident -> TemplateMonad unit
  | tmInferInstance : option reductionStrategy -> term -> 
                      TemplateMonad (option term).

MetaCoq vs Ltac2 对比

特性 Ltac2 MetaCoq
主要用途 策略编程 元编程、代码生成
抽象层次 证明策略层 AST 操作层
类型安全 静态类型 依赖类型
性能 编译执行 解释执行
学习曲线 中等 陡峭
适用场景 自动化证明 代码生成、程序变换

实际选择建议

  1. 使用 Ltac2
    • 需要编写复杂的证明自动化
    • 关注性能和类型安全
    • 想要更好的错误信息
  2. 使用 MetaCoq
    • 需要生成 Coq 代码
    • 实现领域特定语言
    • 开发验证工具
    • 需要深度内省 Coq 项
  3. 结合使用
    (* MetaCoq 生成定理和证明框架 *)
    MetaCoq Run (generate_lemmas ...).
    
    (* Ltac2 完成具体证明 *)
    Ltac2 prove_generated_lemmas () := ...

实战示例:自动生成数据结构操作

From MetaCoq.Template Require Import All.

(* 为任意数据类型生成 fold 函数 *)
Definition generate_fold (ind_name : string) : TemplateMonad unit :=
  (* 获取归纳类型信息 *)
  ind_info <- tmQuoteInductive ind_name ;;
  match ind_info with
  | Build_mutual_inductive_body _ _ bodies _ =>
    match bodies with
    | [body] =>
      (* 构造 fold 函数的类型 *)
      let fold_type := ... in
      (* 构造 fold 函数的定义 *)
      let fold_body := ... in
      (* 定义 fold 函数 *)
      tmDefinition (ind_name ++ "_fold") (Some fold_type) fold_body
    | _ => tmFail "Multiple mutually inductive types not supported"
    end
  end.

(* 使用示例 *)
Inductive tree (A : Type) :=
  | Leaf : A -> tree A
  | Node : tree A -> tree A -> tree A.

MetaCoq Run (generate_fold "tree").
(* 自动生成 tree_fold 函数 *)

总结

MetaCoq 为 Coq 提供了强大的元编程能力,使得:

  • 可以在 Coq 内部分析和生成 Coq 代码
  • 能够实现复杂的代码变换和优化
  • 支持开发经过验证的工具和编译器
  • 为领域特定语言提供实现基础

虽然学习曲线较陡,但 MetaCoq 在需要深度元编程的场景中是不可替代的工具。