Skip to content
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 代码。 然而,直接抽取存在性能权衡:

  1. 皮亚诺自然数(Unary Nat):Coq 的 nat 在内存中是由单向指针链表表示的单项结构,数值 7 是一个 7 层深的指针链,单次比较耗费 O(n) 时间;
  2. 大整数(ZArith / Z):提供基于二进制的对数时间表示,但无法直接匹配现代 CPU 的单周期机器字指令;
  3. 机器字整数(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 m

2. 抽取版插入排序 (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
  sorry

3. 抽取版二叉搜索树 (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

基于 MIT 与 Software Foundations 原版教育许可协议开源