使用Fix或Program Fixpoint在Coq中编写基础良好的程序

使用Fix或Program Fixpoint在Coq中编写基础良好的程序,coq,Coq,按照Chlipala一章中给出的示例,我正在尝试编写mergesort算法 这是我的密码 Require Import Nat. Fixpoint insert (x:nat) (l: list nat) : list nat := match l with | nil => x::nil | y::l' => if leb x y then x::l else y::(insert x l

按照Chlipala一章中给出的示例,我正在尝试编写mergesort算法

这是我的密码

Require Import Nat.

Fixpoint insert (x:nat) (l: list nat) : list nat :=
  match l with
  | nil => x::nil
  | y::l' => if leb x y then
              x::l
            else
              y::(insert x l')
  end.

Fixpoint merge (l1 l2 : list nat) : list nat :=
  match l1 with
  | nil => l2
  | x::l1' => insert x (merge l1' l2)
  end.

Fixpoint split (l : list nat) : list nat * list nat :=
  match l with
  | nil => (nil,nil)
  | x::nil => (x::nil,nil)
  | x::y::l' =>
    let (ll,lr) := split l' in
    (x::ll,y::lr)
  end.

Definition lengthOrder (l1 l2 : list nat) :=
  length l1 < length l2.

Theorem lengthOrder_wf : well_founded lengthOrder.
Admitted.
相反,可以使用命令
程序固定点
定义
,使用术语
Fix
(如Chlipala书中所述)

然而,如果我写这个

Definition mergeSort : list nat -> list nat.
refine (Fix lengthOrder_wf (fun (l: list nat) => list nat)
      (fun (l : list nat) => (fun mergeSort : (forall ls : list nat, lengthOrder ls l -> list nat )=>
                           if leb (length l) 1 then
                             let (ll,lr) := split l in
                             merge (mergeSort ll _) (mergeSort lr _)
                           else
                             l))).
我得到了不可能的目标:

2 subgoals, subgoal 1 (ID 65)

  l : list nat
  mergeSort : forall ls : list nat, lengthOrder ls l -> list nat
  ll, lr : list nat
  ============================
  lengthOrder ll l

subgoal 2 (ID 66) is:
 lengthOrder lr l
这就是为什么Chlipala建议以这种方式更改mergeSort的定义:

Definition mergeSort : list nat -> list nat.
  refine (Fix lengthOrder_wf (fun _ => list nat)
              (fun (ls : list nat)
                 (mergeSort : forall ls' : list nat, lengthOrder ls' ls -> list nat) =>
                 if Compare_dec.le_lt_dec 2 (length ls)
                 then let lss := split ls in
                      merge (mergeSort (fst lss) _) (mergeSort (snd lss) _)
                 else ls)).
这将产生以下目标:

2 subgoals, subgoal 1 (ID 68)

  ls : list nat
  mergeSort : forall ls' : list nat, lengthOrder ls' ls -> list nat
  l : 2 <= length ls
  lss := split ls : list nat * list nat
  ============================
  lengthOrder (fst lss) ls

subgoal 2 (ID 69) is:
 lengthOrder (snd lss) ls
2个子目标,子目标1(ID 68)
ls:列出nat
合并排序:用于所有ls”:列出nat,长度排序器ls->列出nat

l:2很容易看出,为了得到栉孔扇贝的溶液,你需要做两个改变

1) 在执行
split
时,您需要记住
ll
lr
来自split,否则它们将是一些任意列表,不可能比原始列表
l

以下代码无法保存此类信息:

let (ll,lr) := split l in
  merge (mergeSort ll _) (mergeSort lr _)
因此,需要用

let lss := split ls in
  merge (mergeSort (fst lss) _) (mergeSort (snd lss) _)
它保存了我们需要的东西

故障发生的原因是Coq无法记住
ll
lr
来自
split l
,这是因为
let(ll,lr)
只是伪装的
match
(参见手册,)

回想一下,模式匹配的目的是(粗略地说)

  • 解包归纳数据类型的某个值的组件,并将它们绑定到某些名称上(我们需要在我的答案的第二部分中使用它),然后
  • 在相应的模式匹配分支中将原始定义替换为其特殊情况
现在,请注意,在我们对其进行模式匹配之前,
split l
不会出现在目标或上下文中的任何位置。我们只是随意地把它引入定义中。这就是为什么模式匹配没有给我们任何东西——我们不能用目标或上下文中的“特殊情况”(
(ll,lr)
)替换
拆分l
),因为任何地方都没有
拆分l

通过使用逻辑相等(
=
)还有一种替代方法:

这类似于使用
记住
策略。我们已经摆脱了
fst
snd
,但这是一个巨大的过度使用,我不推荐这样做



2) 我们需要证明的另一件事是,当
2
length(fst(split l))
可证明时,
ll
lr
l
短!注意你有
H:2谢谢你的回答。关键是Coq需要的所有假设都应该反映在表达式的类型中,因此,我们需要依赖的引理/结构来实现这一点。最后,这是直截了当的!对通常,与普通函数式编程相比,我们必须更明确地了解信息流。
let lss := split ls in
  merge (mergeSort (fst lss) _) (mergeSort (snd lss) _)
(let (ll, lr) as s return (s = split l -> list nat) := split l in
   fun split_eq => merge (mergeSort ll _) (mergeSort lr _)) eq_refl
Inductive sumbool (A B : Prop) : Set :=
    left : A -> {A} + {B} | right : B -> {A} + {B}
Search (_ -> _ -> {_ <= _} + {_}).

(*
output:
le_lt_dec: forall n m : nat, {n <= m} + {m < n}
le_le_S_dec: forall n m : nat, {n <= m} + {S m <= n}
le_ge_dec: forall n m : nat, {n <= m} + {n >= m}
le_gt_dec: forall n m : nat, {n <= m} + {n > m}
le_dec: forall n m : nat, {n <= m} + {~ n <= m}
*)