我(自己)写一篇关于如何在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.
结尾,它就不会减少。这有关系吗?我想某处G
或g
的定义存在透明度问题......或者我完全错了?
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)).
答案 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
中的负数。