Coq 使用定义良好的归纳法定义的递归函数进行计算
当我使用Coq 使用定义良好的归纳法定义的递归函数进行计算,coq,Coq,当我使用Function在Coq中定义一个非结构化递归函数时,当要求进行特定计算时,结果对象的行为异常。实际上,Eval compute in…指令没有直接给出结果,而是返回一个相当长(通常为170 000行)的表达式。Coq似乎无法计算所有内容,因此返回一个简化(但很长)的表达式,而不仅仅是一个值 问题似乎来自我证明函数生成的义务的方式。首先,我认为问题来自我使用的不透明术语,我将所有引理转换为透明常数。顺便问一下,有没有办法列出术语中出现的不透明术语?或者用其他方法把不透明的引理变成透明的引
Function
在Coq中定义一个非结构化递归函数时,当要求进行特定计算时,结果对象的行为异常。实际上,Eval compute in…
指令没有直接给出结果,而是返回一个相当长(通常为170 000行)的表达式。Coq似乎无法计算所有内容,因此返回一个简化(但很长)的表达式,而不仅仅是一个值
问题似乎来自我证明函数生成的义务的方式。首先,我认为问题来自我使用的不透明术语,我将所有引理转换为透明常数。顺便问一下,有没有办法列出术语中出现的不透明术语?或者用其他方法把不透明的引理变成透明的引理
我接着说,问题更确切地来自于所使用的顺序是有充分根据的证据。但我得到了奇怪的结果
例如,我通过反复应用div2
对自然数定义log2
。定义如下:
Function log2 n {wf lt n} :=
match n with
| 0 => 0
| 1 => 0
| n => S (log2 (Nat.div2 n))
end.
我有两个证明义务。第一种方法检查n
是否尊重递归调用中的关系lt
,并且很容易证明
forall n n0 n1 : nat, n0 = S n1 -> n = S (S n1) -> Nat.div2 (S (S n1)) < S (S n1)
intros. apply Nat.lt_div2. apply le_n_S. apply le_0_n.
这里,引理1是对自然数的有充分根据的归纳的证明。在这里,我可以再次使用已经存在的引理,例如lt\u wf\u ind
,lt\u wf\u rec
,lt\u wf\u rec1
位于Coq.Arith.wf\u nat
中,甚至位于的良好基础的lt\u wf
。第一个不起作用,似乎这是因为它是不透明的。另外三个在工作
我试图用自然数的标准归纳法直接证明它,nat\u ind
。这使得:
Lemma lemma1 : forall n (P:nat -> Prop),
(forall n, (forall p, p < n -> P p) -> P n) -> P n.
Proof.
intros n P H. pose proof (nat_ind (fun n => forall p, p < n -> P p)).
simpl in H0. apply H0 with (n:=S n).
- intros. inversion H1.
- intros. inversion H2.
+ apply H. exact H1.
+ apply H1. assumption.
- apply le_n.
Defined.
引理1:forall n(P:nat->Prop),
(对于所有n,(对于所有p,ppp)->pn)->pn。
证明。
介绍n P H.姿势证明(自然属性(乐趣n=>所有P,PP))。
H0中的siml。使用(n:=sn)应用H0。
-介绍。反转H1。
-介绍。反转H2。
+应用H.精确H1。
+应用H1。假设。
-应用leu\n。
定义
有了这个证明(以及它的一些变体),log2
具有相同的奇怪行为。这个证明似乎只使用透明对象,所以问题可能不在这里
如何定义返回特定值的可理解结果的函数
?Coq中有充分依据的递归定义的函数的缩减行为通常不是很好,即使您声明证明是透明的。原因是,有充分根据的论点通常需要用复杂的证明条件来完成。由于这些证明术语最终出现在有充分依据的递归定义中,“简化”您的函数将使所有这些证明术语出现,正如您所注意到的
更容易依靠自定义策略和引理来减少以这种方式定义的函数。首先,我建议使用程序固定点
而不是函数
,因为后者更老,而且(我认为)维护得不太好。因此,您将得到如下定义:
Require Import Coq.Numbers.Natural.Peano.NPeano.
Require Import Coq.Program.Wf.
Require Import Coq.Program.Tactics.
Program Fixpoint log2 n {wf lt n} :=
match n with
| 0 => 0
| 1 => 0
| n => S (log2 (Nat.div2 n))
end.
Next Obligation.
admit.
Qed.
现在,您只需要使用program\u siml
策略来简化对log2
的调用。下面是一个例子:
Lemma foo : log2 4 = 2.
Proof.
program_simpl.
Qed.
我已经设法找到了引起麻烦的地方:在lemma1
中的inversionh2.
。事实证明,我们不需要案例分析,直觉
可以完成证明(它在H2
上的模式不匹配):
我相信
Xavier Leroy的博客文章解释了这种行为
它消除了头部之间相等的证明,然后在尾部上递归,最后决定是否生成左或右。这使得最终结果的左/右数据部分依赖于证明项,通常不会减少
在我们的例子中,我们消除了引理1
证明中的不等式证明(倒置H2.
),而函数
机制使我们的计算依赖于证明项。因此,当n>1时,计算器无法继续
引理主体中的倒数H1。
不影响计算的原因是,对于n=0
和n=1
,log2n
在匹配
表达式中定义为基本情况
为了说明这一点,让我举一个例子,我们可以防止对我们选择的任何值n
和n+1
计算log2n
,其中n>1
,而不是其他任何值
如果你在log2
的定义中使用这个修改过的引理,你会发现除了n=4
和n=5
之外,它到处都在计算。几乎在所有地方——使用大的nat
s计算可能会导致堆栈溢出或分段错误,正如Coq警告我们的那样:
警告:使用时发生堆栈溢出或分段错误
nat中的大量数据(观察到的阈值可能从5000到70000不等
取决于系统限制和执行的命令)
为了使log2
适用于n=4
和n=5
,即使对于上述“有缺陷”的证明,我们也可以这样修改log2
Function log2 n {wf lt n} :=
match n with
| 0 => 0
| 1 => 0
| 4 => 2
| 5 => 2
| n => S (log2 (Nat.div2 n))
end.
在末尾添加必要的证据
“有充分根据的”证明必须是透明的,并且不能依赖于证明对象上的模式匹配,因为
函数
机制实际上使用lt\u wf
引理来计算递减终止保护。如果我们看一下由Eval
生成的术语(在求值无法生成nat
的情况下),我们将看到以下内容:
fix Ffix (x : nat) (x0 : Acc (fun x0 x1 : nat => S x0 <= x1) x) {struct x0}
fix-Ffix(x:nat)(x0:Acc(fun-x0-x1:nat=>S-x0好的,这是另一种选择。但是我想知道这
Lemma lt_wf2 : well_founded lt.
Proof.
unfold well_founded; intros n.
induction n; constructor; intros k Hk.
- inversion Hk.
- constructor; intros m Hm.
apply IHn; omega.
(* OR: apply IHn, Nat.lt_le_trans with (m := k); auto with arith. *)
Defined.
Lemma lt_wf2' : well_founded lt.
Proof.
unfold well_founded; intros n.
induction n; constructor; intros k Hk.
- inversion Hk. (* n = 0 *)
- destruct n. intuition. (* n = 1 *)
destruct n. intuition. (* n = 2 *)
destruct n. intuition. (* n = 3 *)
destruct n. inversion Hk; intuition. (* n = 4 and n = 5 - won't evaluate *)
(* n > 5 *)
constructor; intros m Hm; apply IHn; omega.
Defined.
Function log2 n {wf lt n} :=
match n with
| 0 => 0
| 1 => 0
| 4 => 2
| 5 => 2
| n => S (log2 (Nat.div2 n))
end.
fix Ffix (x : nat) (x0 : Acc (fun x0 x1 : nat => S x0 <= x1) x) {struct x0}