Coq无法在Z上计算有根据的函数,但它适用于nat

时间:2017-05-25 17:47:31

标签: recursion coq totality

我(自己)写一篇关于如何在Coq中做有根据的递归的解释。 (参见Coq' Art book,第15.2章)。首先,我根据nat创建了一个示例函数,并且工作正常,但后来我再次为Z做了一次,当我使用Compute来评估它时,它并没有一直减少到Z值。为什么呢?

这是我的示例(我将文本放在注释中,以便将整个内容复制粘贴到编辑器中):

(*有充分理由的递归测试*)

(* TL; DR:做有根据的递归,    首先创造'功能性'然后    使用创建递归函数    Acc_iter,可访问关系的迭代器 *)

(*作为一个例子,计算从1到n的系列之和,    像这样的草图:

修复f n:=(如果n = 0则为0,否则为n + f(n-1))

现在,让在n上使用结构递归。

相反,我们在n上使用有根据的递归,    使用那个小于(' lt')的关系    wellfounded。函数f终止是因为    递归调用是在结构上进行的    较小的术语(在递减的Acc链中)。 *)

(*首先我们为nat *做)

Require Import Arith.Arith.
Require Import Program.Utils. (* for 'dec' *)
Require Import Wellfounded.

(*来自证明关系良好的证据,    我们可以得到一个特定元素的证明    在其域中可访问。

此处的检查命令不是必需的,仅供参考    亲爱的读者,文档。 *)

Check well_founded : forall A : Type, (A -> A -> Prop) -> Prop.
Check lt_wf : well_founded lt.
Check (lt_wf 4711 : Acc lt 4711).

(*首先为f定义一个'功能' F。它是一个函数    采用函数F_rec进行递归调用'作为一个论点。    因为我们需要知道n<>第二个分支中的0    我们用'dec'将布尔if条件转换为a    sumbool。我们将这些信息发送到分支机构。

我们用精炼来写大部分,留下一些漏洞    以后要用战术填补。 *)

Definition F (n:nat) (F_rec : (forall y : nat, y < n -> nat)): nat.
  refine ( if dec (n =? 0) then 0 else n + (F_rec (n-1) _ ) ).

  (* now we need to show that n-1 < n, which is true for nat if n<>0 *)
  destruct n; now auto with *.
Defined.

(*函数可以由迭代器用来调用f    根据需要多次。

旁注:可以创建一个占用最大值的迭代器    递归深度d作为nat参数,并在d上递归,但是    然后必须提供d,以及一个默认值&#39;    如果d达到零并且必须终止,则返回    早。

有充分理由的递归的巧妙之处在于    迭代器可以证明良好的证据    并且不需要任何其他结构或默认值    保证它会终止。  *)

(* Acc_iter的类型非常多毛*)

Check Acc_iter :
      forall (A : Type) (R : A -> A -> Prop) (P : A -> Type),
       (forall x : A, (forall y : A, R y x -> P y) -> P x) -> forall x : A, Acc R x -> P x.

(* P是因为返回类型可能取决于参数,
   但在我们的例子中,f:nat-&gt; nat,而R = lt,所以我们有  *)

Check Acc_iter (R:=lt) (fun _:nat=>nat) :
  (forall n : nat, (forall y : nat, y < n -> nat) -> nat) ->
   forall n : nat, Acc lt n -> nat.

(*这里第一个参数是迭代器所用的功能,    第二个参数n是f的输入,第三个参数是    证明n是可访问的。    迭代器返回应用于n的f的值。

一些Acc_iter的参数是隐含的,有些可以推断出来。    因此我们可以简单地定义f如下: *)

Definition f n := Acc_iter _ F (lt_wf n).

(*它就像一个魅力*)

Compute (f 50). (* This prints 1275 *)
Check eq_refl : f 50 = 1275.

(*现在让我们为Z做。这里我们不能使用LT,    或lt_wf,因为他们是为nat。对于Z我们    可以使用Zle和(Zwf c),它采取下限。    它需要一个我们知道该函数的下限    将始终终止以保证终止。    在这里我们使用(Zwf 0)来说我们的函数会    总是等于或低于0.我们也必须    将if语句更改为&#39;如果n <= 0则为0,否则...&#39;    所以我们为小于零的参数返回零。  *)

Require Import ZArith.
Require Import Zwf.

Open Scope Z.

(*现在我们根据功能G *定义函数g)

Definition G (n:Z) (G_rec :  (forall y : Z, Zwf 0 y n -> Z)) : Z.
  refine (if dec (n<?0) then 0 else n + (G_rec (n-1) _ )).

  (* now we need to show that n-1 < n *)
  now split; [ apply Z.ltb_ge | apply Z.lt_sub_pos].
Defined.

Definition g n := Acc_iter _ G (Zwf_well_founded 0 n).

(*但现在我们无法计算!*)

Compute (g 1).

(*我们只是从

开始获得一个巨大的术语
     = (fix
        Ffix (x : Z)
             (x0 : Acc
                     (fun x0 x1 : Z =>
                      (match x1 with
                       | 0 => Eq
                       | Z.pos _ => Lt
                       | Z.neg _ => Gt
                       end = Gt -> False) /\
                      match x0 with
                      | 0 => match x1 with
                             | 0 => Eq
                             | Z.pos _ => Lt
                             | Z.neg _ => Gt
                             end
                      | Z.pos x2 =>

    ...

 end) 1 (Zwf_well_founded 0 1)
     : (fun _ : Z => Z) 1
   ) 

评论:我注意到Zwf_well_founded在库中被定义为Opaque,因此我尝试通过复制证据并使用Transparent结束引理来使其成为Defined.而不是Qed.,但这没有帮助...

补充观察:

如果我使用f'nat定义Fixpoint,并递归 可访问性证明,并以Defined. 结束然后计算。但如果我以Qed.结尾,它就不会减少。这有关系吗?我想某处Gg的定义存在透明度问题......或者我完全错了?

Fixpoint f' (n:nat) (H: Acc lt n) : nat.
  refine (if dec (n<=?0) then 0 else n + (f' (n-1) (Acc_inv H _))).
  apply Nat.leb_gt in e.
  apply Nat.sub_lt; auto with *.
Defined.  (* Compute (f' 10 (lt_wf 10)). doesn't evaluate to a nat if ended with Qed. *)

无论如何,我的问题仍然存在于Z

Fixpoint g' (n:Z) (H: Acc (Zwf 0) n) : Z.
  refine (if dec (n<=?0) then 0 else n + (g' (n-1) (Acc_inv H _))).
  split; now apply Z.leb_gt in e; auto with *.
Defined.

Compute (g' 10 (Zwf_well_founded 0 10)).

1 个答案:

答案 0 :(得分:4)

Zwf_well_founded透明无助,因为它在标准库中的定义方式:

Lemma Zwf_well_founded : well_founded (Zwf c).
...
    case (Z.le_gt_cases c y); intro; auto with zarith.
...
Qed.

如果用

替换上述证明中的行
     case (Z_le_gt_dec c y); intro; auto with zarith.

并将Qed.替换为Defined.(您已经做过),一切都应该有效。这是因为原始证明取决于逻辑术语,并且阻止评估者进行模式匹配,因为逻辑实体Z.le_gt_cases是不透明的,而计算实体Z_le_gt_dec是透明的。请参阅Xavier Leroy撰写的Using Coq's evaluation mechanisms in anger博客文章。您可能还会发现Gregory Malecha发布的有用Qed Considered Harmful帖子。

而不是修改Zwf_well_founded的证明,您可以像这样重复使用Zlt_0_rec

Require Import Coq.ZArith.ZArith.

Open Scope Z.

Definition H (x:Z) (H_rec : (forall y : Z, 0 <= y < x -> Z)) (nonneg : 0 <= x) : Z.
  refine (if Z_zerop x then 0 else x + (H_rec (Z.pred x) _ )).
  auto with zarith.
Defined.

Definition h (z : Z) : Z :=
  match Z_lt_le_dec z 0 with left _ => 0 | right pf => (Zlt_0_rec _ H z pf) end.

Check eq_refl : h 100 = 5050.

它不太方便,因为现在我们必须处理h中的负数。