lean
import SFLean4.VFA.Perm
namespace VFA.Extract
open VFA.Perm《软件基础》(Software Foundations) 卷三:函数式算法验证 (VFA)
第十一章:运行 Coq 程序与代码抽取 (Extract: Running Coq Programs in OCaml)
形式化验证的核心价值之一在于:不仅能够用逻辑证明算法的正确性性质,还能够将经过验证的算法无缝提取为高效的通用编程语言代码。
在 Coq 中,Extraction 功能可以将 Gallina 函数抽取为 OCaml 或 Haskell 代码。 然而,直接抽取存在性能权衡:
- 皮亚诺自然数(Unary Nat):Coq 的
nat在内存中是由单向指针链表表示的单项结构,数值 7 是一个 7 层深的指针链,单次比较耗费时间; - 大整数(ZArith / Z):提供基于二进制的对数时间表示,但无法直接匹配现代 CPU 的单周期机器字指令;
- 机器字整数(Lightweight Extraction to Machine Integers): 在证明器中将机器整数抽象为一个公理化类型
int,并通过抽象函数Abs : int → Int映射到数学整数进行推理, 而在抽取代码时直接内联映射为目标语言的原生机器字整数(如 OCaml 的int,在现代 64 位系统上为 63 位整数)。
NOTE
在 Lean 4 中,形式化验证与系统级编程合二为一: Lean 4 本身就是一个高性能编译型语言(经由 Lean 4 IR 直接编译为 C99 代码,并由 LLVM 编译为机器码), 同时拥有原生开箱支持的 Nat(小整数内联 unboxed 字长,溢出自动切入 GMP)、固定精度机器字 UInt64/Int64 等。 为保持与 Coq 源码 100% 对齐,本章忠实建立 Coq 的轻量机器整数抽象层 int 与基于 Abs 的逻辑规约。
1. 原生机器整数的公理化抽象层 (Lightweight Extraction to int)
lean
/-- 抽象机器整数类型(在抽取时内联为目标语言的原生字长整数) -/
opaque int : Type
/-- 机器整数到数学整数的抽象映射函数 -/
opaque Abs : int → Int
/-- 抽象映射单射公理:若两个机器整数对应的数学整数相同,则它们在物理表示上等价 -/
axiom Abs_inj : ∀ (n m : int), Abs n = Abs m → n = m
/-- 机器整数严格小于布尔比较判定 -/
opaque ltb : int → int → Bool
axiom ltb_lt : ∀ (n m : int), ltb n m = true ↔ Abs n < Abs m
/-- 机器整数小于等于布尔比较判定 -/
opaque leb : int → int → Bool
axiom leb_le : ∀ (n m : int), leb n m = true ↔ Abs n ≤ Abs m2. 抽取版插入排序 (Insertion Sort, Extracted)
lean
/-- 基于机器整数的高效元素插入 -/
def ins_int (i : int) (l : List int) : List int :=
match l with
| [] => [i]
| h :: t => if leb i h then i :: h :: t else h :: ins_int i t
/-- 基于机器整数的插入排序主函数 -/
def sort_int (l : List int) : List int :=
match l with
| [] => []
| h :: t => ins_int h (sort_int t)
/-- 机器整数列表的有序性谓词(依据其数学抽象值判定) -/
inductive sorted : List int → Prop where
| sorted_nil : sorted []
| sorted_1 (x : int) : sorted [x]
| sorted_cons (x y : int) (l : List int) :
Abs x ≤ Abs y → sorted (y :: l) → sorted (x :: y :: l)
/-- #### 练习:3 星 (sort_int_correct)
证明基于机器整数的插入排序算法满足完全正确性规约(保持置换性与有序性)。 -/
theorem sort_int_correct : ∀ (al : List int),
Permutation al (sort_int al) ∧ sorted (sort_int al) := by
sorry3. 抽取版二叉搜索树 (Binary Search Trees, Extracted)
lean
/-- 键类型直接定义为机器整数 -/
abbrev key : Type := int
/-- 基于机器整数键的二叉树 -/
inductive tree (V : Type) : Type where
| E : tree V
| T : tree V → key → V → tree V → tree V
def empty_tree {V : Type} : tree V := tree.E
/-- 基于机器整数快速比较的树查找操作 -/
def lookup {V : Type} (default : V) (x : key) (t : tree V) : V :=
match t with
| tree.E => default
| tree.T l k v r =>
if ltb x k then lookup default x l
else if ltb k x then lookup default x r
else v
/-- 基于机器整数快速比较的持久化树插入操作 -/
def insert {V : Type} (x : key) (v : V) (t : tree V) : tree V :=
match t with
| tree.E => tree.T tree.E x v tree.E
| tree.T l y v' r =>
if ltb x y then tree.T (insert x v l) y v' r
else if ltb y x then tree.T l y v' (insert x v r)
else tree.T l x v r
/-- 快速尾递归中序遍历辅助函数 -/
def elements_aux {V : Type} (t : tree V) (acc : List (key × V)) : List (key × V) :=
match t with
| tree.E => acc
| tree.T l k v r => elements_aux l ((k, v) :: elements_aux r acc)
/-- 转换为关联列表的快速顶层调用 -/
def elements {V : Type} (t : tree V) : List (key × V) :=
elements_aux t []
theorem lookup_empty : ∀ (V : Type) (default : V) (k : key),
lookup default k empty_tree = default := by
intro V default k
rfl
/-- #### 练习:1 星 (lookup_insert_eq)
代数性质:新插入的键立即查询必命中该值。 -/
theorem lookup_insert_eq :
∀ (V : Type) (default : V) (t : tree V) (k : key) (v : V),
lookup default k (insert k v t) = v := by
sorry
/-- #### 练习:2 星 (lookup_insert_neq)
代数性质:对键 `k` 的插入不会影响对不同键 `k'` 的查询结果。 -/
theorem lookup_insert_neq :
∀ (V : Type) (default : V) (t : tree V) (k k' : key) (v : V),
k ≠ k' → lookup default k' (insert k v t) = lookup default k' t := by
sorry
end VFA.Extract