教coq检查终止

教coq检查终止,coq,termination,totality,Coq,Termination,Totality,与其他许多参数不同,Coq接受一个可选的显式参数,该参数可用于指示不动点定义的递减结构 根据Gallina规范第1.3.4节 Fixpoint ident params {struct ident0 } : type0 := term0 定义语法。但从中,我们知道它必须是一个标识符,而不是一个通用的度量 然而,一般来说,存在递归函数,终止不是很明显,或者实际上是,但终止检查器很难找到递减结构。例如,以下程序交错两个列表 Fixpoint interleave (A : Set) (l1 l2

与其他许多参数不同,Coq接受一个可选的显式参数,该参数可用于指示不动点定义的递减结构

根据Gallina规范第1.3.4节

Fixpoint ident params {struct ident0 } : type0 := term0
定义语法。但从中,我们知道它必须是一个标识符,而不是一个通用的度量

然而,一般来说,存在递归函数,终止不是很明显,或者实际上是,但终止检查器很难找到递减结构。例如,以下程序交错两个列表

Fixpoint interleave (A : Set) (l1 l2 : list A) : list A :=
  match l1 with
  | [] => []
  | h :: t => h :: interleave l2 t
  end
这个函数显然终止了,而Coq就是无法理解它。原因是无论是
l1
还是
l2
都不是每个周期都在减少。但是,如果我们考虑一个度量,定义为“代码>长度L1+长度L2< /代码>?然后这个度量明显减少了每次递归


因此,我的问题是,在复杂的情况下,如果代码不容易以可终止检查的方式组织,您如何教育coq并说服它接受不动点定义?

您可以使用一种称为
度量值的方法,而不是用结构参数来终止。为此,我认为您必须使用
程序固定点
机制,该机制有点复杂,会使您的证明看起来更难看(因为它会根据您提供的证明生成结构递归,因此您实际使用的函数与您编写的函数不完全相同)

详情如下:

它看起来也像是一种叫做方程的东西,可以处理度量? 查阅

您有多个选项,所有选项最终都归结为结构递归

序言 结构递归 有时,您可以以结构递归的方式重新制定算法:

Fixpoint interleave1 {A} (l1 l2 : list A) {struct l1} : list A :=
  match l1, l2 with
  | [], _ => l2
  | _, [] => l1
  | h1 :: t1, h2 :: t2 => h1 :: h2 :: interleave1 t1 t2
  end.
顺便说一句,在某些情况下,您可以对嵌套的
fix
es使用技巧——请参见(仅对
Fixpoint
不起作用)

程序定点
您可以使用
程序固定点
机制,让您自然编写程序,然后证明它总是终止

From Coq Require Import Program Arith.

Program Fixpoint interleave2 {A} (l1 l2 : list A) 
  {measure (length l1 + length l2)} : list A :=
  match l1 with
  | [] => l2
  | h :: t => h :: interleave2 l2 t
  end.
Next Obligation. simpl; rewrite Nat.add_comm; trivial with arith. Qed.
功能
另一个选项是使用
函数
命令,与
程序定点
相比,该命令可能会受到一定限制。你可以了解更多关于他们的差异

插件 这是一个外部插件,它解决了在Coq中定义函数的许多问题,包括依赖类型和终止

From Equations Require Import Equations.

Equations interleave4 {A} (l1 l2 : list A) : list A :=
interleave4 l1 l2 by rec (length l1 + length l2) lt :=
interleave4 nil l2 := l2;
interleave4 (cons h t) l2 := cons h (interleave4 l2 t).
Next Obligation. rewrite Nat.add_comm; trivial with arith. Qed.
如果你申请,上面的代码是有效的

Fix
/
Fix\F\u 2
组合器 如果您遵循“关于
mergeSort
功能”中的链接,可以了解更多有关此(手动)方法的信息。顺便说一句,如果应用我前面提到的嵌套
Fix
技巧,则可以在不使用
Fix
的情况下定义
mergeSort
函数。这是一个使用
Fix\u F_2
组合器的解决方案,因为我们有两个参数,而不是像
mergeSort
那样的参数:

Definition ordering {A} (l1 l2 : list A * list A) : Prop :=
  length (fst l1) + length (snd l1) < length (fst l2) + length (snd l2).

Lemma ordering_wf' {A} : forall (m : nat) (p : list A * list A),
    length (fst p) + length (snd p) <= m -> Acc (@ordering A) p.
Proof.
  unfold ordering; induction m; intros p H; constructor; intros p'.
  - apply Nat.le_0_r, Nat.eq_add_0 in H as [-> ->].
    intros contra%Nat.nlt_0_r; contradiction.
  - intros H'; eapply IHm, Nat.lt_succ_r, Nat.lt_le_trans; eauto.
Defined.

Lemma ordering_wf {A} : well_founded (@ordering A).
Proof. now red; intro ; eapply ordering_wf'. Defined.

(* it's in the stdlib but unfortunately opaque -- this blocks evaluation *)
Lemma destruct_list {A} (l : list A) :
  { x:A & {tl:list A | l = x::tl} } + { l = [] }.
Proof.
  induction l as [|h tl]; [right | left]; trivial.
  exists h, tl; reflexivity.
Defined.

Definition interleave5 {A} (xs ys : list A) : list A.
  refine (Fix_F_2 (fun _ _ => list A)
    (fun (l1 l2 : list A)
       (interleave : (forall l1' l2', ordering (l1', l2') (l1, l2) -> list A)) =>
       match destruct_list l1 with
       | inright _ => l2
       | inleft pf => let '(existT _ h (exist _ tl eq)) := pf in
                     h :: interleave l2 tl _
       end) (ordering_wf (xs,ys))).
Proof. unfold ordering; rewrite eq, Nat.add_comm; auto.
Defined.

练习:如果您注释掉
destruct\u list
lemma,最后一次检查会发生什么情况?

您是否在询问可以用来说服Coq您的函数终止的工具?还是别的什么?比如如何使用
Fixpoint
fix
原语来实现这一点?@AntonTrunov我想问的是,如果可能的话,我是否可以证明函数终止,如何将它教给coq。为了这个例子,如何让COQ接受这个交织定义,而不改变算法的基本思想?你会考虑使用<代码>程序不动点> />代码>函数> <代码> >代码>修复> <代码>组合器/<代码>方程< /代码>插件作为解决方案吗?使用这些工具会在函数中添加一个额外的参数,其目的是让Coq相信函数正在终止。@AntonTrunov我不知道这些。我来看看。感谢上帝,这是一篇如此精练的教育性文章。
From Equations Require Import Equations.

Equations interleave4 {A} (l1 l2 : list A) : list A :=
interleave4 l1 l2 by rec (length l1 + length l2) lt :=
interleave4 nil l2 := l2;
interleave4 (cons h t) l2 := cons h (interleave4 l2 t).
Next Obligation. rewrite Nat.add_comm; trivial with arith. Qed.
Definition ordering {A} (l1 l2 : list A * list A) : Prop :=
  length (fst l1) + length (snd l1) < length (fst l2) + length (snd l2).

Lemma ordering_wf' {A} : forall (m : nat) (p : list A * list A),
    length (fst p) + length (snd p) <= m -> Acc (@ordering A) p.
Proof.
  unfold ordering; induction m; intros p H; constructor; intros p'.
  - apply Nat.le_0_r, Nat.eq_add_0 in H as [-> ->].
    intros contra%Nat.nlt_0_r; contradiction.
  - intros H'; eapply IHm, Nat.lt_succ_r, Nat.lt_le_trans; eauto.
Defined.

Lemma ordering_wf {A} : well_founded (@ordering A).
Proof. now red; intro ; eapply ordering_wf'. Defined.

(* it's in the stdlib but unfortunately opaque -- this blocks evaluation *)
Lemma destruct_list {A} (l : list A) :
  { x:A & {tl:list A | l = x::tl} } + { l = [] }.
Proof.
  induction l as [|h tl]; [right | left]; trivial.
  exists h, tl; reflexivity.
Defined.

Definition interleave5 {A} (xs ys : list A) : list A.
  refine (Fix_F_2 (fun _ _ => list A)
    (fun (l1 l2 : list A)
       (interleave : (forall l1' l2', ordering (l1', l2') (l1, l2) -> list A)) =>
       match destruct_list l1 with
       | inright _ => l2
       | inleft pf => let '(existT _ h (exist _ tl eq)) := pf in
                     h :: interleave l2 tl _
       end) (ordering_wf (xs,ys))).
Proof. unfold ordering; rewrite eq, Nat.add_comm; auto.
Defined.
Check eq_refl : interleave1 [1;2;3] [4;5;6] = [1;4;2;5;3;6].
Check eq_refl : interleave2 [1;2;3] [4;5;6] = [1;4;2;5;3;6].
Check eq_refl : interleave3 ([1;2;3], [4;5;6]) = [1;4;2;5;3;6].
Fail Check eq_refl : interleave4 [1;2;3] [4;5;6] = [1;4;2;5;3;6]. (* Equations plugin *)
Check eq_refl : interleave5 [1;2;3] [4;5;6] = [1;4;2;5;3;6].