我已经成功地找出了造成麻烦的地方:它inversion H2.
in lemma1
。事实证明我们不需要案例分析intuition
可以完成证明(它不进行模式匹配H2
):
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. intuition.
- apply le_n.
Defined.
如果我们使用lemma1
有了这个证明,计算log2 10
结果是3
.
顺便说一句,这是我的版本lt_wf2
(它也可以让我们计算):
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.
我相信在愤怒中使用 Coq 的评估机制Xavier Leroy 的博客文章解释了这种行为。
在递归尾部并最终决定是产生左还是右之前,它消除了头之间的相等性证明。这使得最终结果的左/右数据部分取决于证明项,而证明项通常不会减少!
在我们的例子中,我们消除了不平等的证明(inversion H2.
) 的证明lemma1
和Function
机制使我们的计算依赖于证明项。因此,当 n > 1 时,评估器无法继续。
以及原因inversion H1.
在引理的主体中不影响计算的是n = 0
and n = 1
, log2 n
被定义在match
表达式作为基本情况。
为了说明这一点,让我举一个例子,当我们可以阻止评估log2 n
对任何值n
and n + 1
我们的选择,其中n > 1
and 无处!
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.
如果您在定义中使用这个修改后的引理log2
你会发现它在任何地方都进行计算,除了n = 4
and n = 5
。好吧,几乎无处不在——大型计算nat
s 可以导致堆栈溢出或分段错误,正如 Coq 警告我们的那样:
警告:使用时会发生堆栈溢出或分段错误
nat 中的大量数字(观察到的阈值可能从 5000 到 70000 不等)
取决于您的系统限制和执行的命令)。
并让log2
为。。。工作n = 4
and 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.
最后添加必要的证明。
The "well-founded" proof must be transparent and can't rely on pattern-matching on proof objects because the
Function
mechanism actually uses the
lt_wf
lemma to compute the decreasing termination guard. If we look at the term produced by
Eval
(in a case where evaluation fails to produce a
nat
), we'll see something along these lines:
fix Ffix (x : nat) (x0 : Acc (fun x0 x1 : nat => S x0 <= x1) x) {struct x0}
很容易看出x0 : Prop
,所以在提取功能程序时它会被删除log2
比如说 OCaml,但是 Coq 的内部评估机制必须使用它来确保终止。