如何处理Coq中的Program Fixpoint生成的非常大的术语? [英] How to deal with really large terms generated by Program Fixpoint in Coq?
问题描述
我正在尝试在Coq中定义并证明其正确性,该功能可以有效地区分两个已排序的列表。由于它并不总是在结构上较小的术语上递归(第一个或第二个列表较小), Fixpoint
不会接受它,因此我尝试使用程序定位点
。
I'm attempting to define and prove correct in Coq a function that efficiently diffs two sorted lists. As it does not always recurse on a structurally smaller term (either the first or second list is smaller), Fixpoint
won't accept it, so I'm attempting to use Program Fixpoint
instead.
尝试使用战术 simpl证明函数的属性时
或 program_simpl
,Coq花了几分钟的时间进行计算,然后产生一个数百行长的宏术语。我想知道我是使用错误的方式 Program Fixpoint
,还是在推理时是否应该使用其他策略而不是简化方法?
When attempting to prove a property of the function using the tactic simpl
or program_simpl
, Coq spends minutes computing and then produces a giant term, hundreds of lines long. I was wondering if I'm using Program Fixpoint
the wrong way, or alternatively if there are other tactics that should be used instead of simplification when reasoning about it?
我还想知道,在这样的参数中包括正确性所需的属性是一种好习惯,还是最好有一个单独的包装函数,将正确性属性作为参数,并且使此函数仅将两个列表进行比较?
I also wondered if it's good practice to include the required properties for correctness in params like this, or would it be better to have a separate wrapper function that takes the correctness properties as params, and make this function just take the two lists to be diffed?
请注意,我确实尝试定义了 make_diff
,仅使用l1和l2作为参数,并固定了类型 A
和关系 R
,但这仍然会产生一个当应用 program_simpl
或 simpl
策略时的巨大名词。
Note that I did try defining a simpler version of make_diff
, which only took l1 and l2 as parameters and fixed the type A
and relation R
, but this still produced a gigantic term when the program_simpl
or simpl
tactics were applied.
*编辑:我的包含项是(尽管这里可能不需要全部):
* my includes are (although they may not all be required here):
Require Import Coq.Sorting.Sorted.
Require Import Coq.Lists.List.
Require Import Coq.Relations.Relation_Definitions.
Require Import Recdef.
Require Import Coq.Program.Wf.
Require Import Coq.Program.Tactics.
代码:
Definition is_decidable (A : Type) (R : relation A) := forall x y, {R x y} + {~(R x y)}.
Definition eq_decidable (A : Type) := forall (x y : A), { x = y } + { ~ (x = y) }.
Inductive diff (X: Type) : Type :=
| add : X -> diff X
| remove : X -> diff X
| update : X -> X -> diff X.
Program Fixpoint make_diff (A : Type)
(R : relation A)
(dec : is_decidable A R)
(eq_dec : eq_decidable A)
(trans : transitive A R)
(lt_neq : (forall x y, R x y -> x <> y))
(l1 l2 : list A)
{measure (length l1 + length l2) } : list (diff A) :=
match l1, l2 with
| nil, nil => nil
| nil, (new_h::new_t) => (add A new_h) :: (make_diff A R dec eq_dec trans lt_neq nil new_t)
| (old_h::old_t), nil => (remove A old_h) :: (make_diff A R dec eq_dec trans lt_neq old_t nil)
| (old_h::old_t) as old_l, (new_h::new_t) as new_l =>
if dec old_h new_h
then (remove A old_h) :: make_diff A R dec eq_dec trans lt_neq old_t new_l
else if eq_dec old_h new_h
then (update A old_h new_h) :: make_diff A R dec eq_dec trans lt_neq old_t new_t
else (add A new_h) :: make_diff A R dec eq_dec trans lt_neq old_l new_t
end.
Next Obligation.
Proof.
simpl.
generalize dependent (length new_t).
generalize dependent (length old_t).
auto with arith.
Defined.
Next Obligation.
Proof.
simpl.
generalize dependent (length new_t).
generalize dependent (length old_t).
auto with arith.
Defined.
推荐答案
在这种情况下,我们可以摆脱程序修订点
,并使用简单的修复点
。由于在每个递归调用中我们都在第一个列表的末尾或第二个列表的末尾调用 make_diff
,因此我们可以嵌套两个定点函数,如下所示。 (我在这里使用 Section
机制来避免传递太多相同的参数)
In this particular case we can get rid of Program Fixpoint
and use plain simple Fixpoint
. Since at each recursive call we invoke make_diff
either on the tail of the first list or on the tail of the second list, we can nest two fixed-point functions as follows. (I have used the Section
mechanism here to avoid passing too many identical arguments)
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Coq.Relations.Relations.
Section Make_diff.
Variable A : Type.
Variable R : relation A.
Variable dec : is_decidable A R.
Variable eq_dec : eq_decidable A.
Variable trans : transitive A R.
Variable lt_neq : forall x y, R x y -> x <> y.
Fixpoint make_diff (l1 l2 : list A) : list (diff A) :=
let fix make_diff2 l2 :=
match l1, l2 with
| nil, nil => nil
| nil, new_h::new_t => (add A new_h) :: make_diff2 new_t
| old_h::old_t, nil => (remove A old_h) :: make_diff old_t nil
| old_h::old_t, new_h::new_t =>
if dec old_h new_h
then (remove A old_h) :: make_diff old_t l2
else if eq_dec old_h new_h
then (update A old_h new_h) :: make_diff old_t new_t
else (add A new_h) :: make_diff2 new_t
end
in make_diff2 l2.
End Make_diff.
请注意 Section
机制不会在结果签名中包括未使用的参数。这是一个幼稚的测试:
Observe that the Section
mechanism won't include unused parameters in the resulting signature. Here is a naive test:
(* make the first 2 arguments implicit *)
Arguments make_diff [A R] _ _ _ _.
Require Import Coq.Arith.Arith.
Compute make_diff lt_dec Nat.eq_dec [1;2;3] [4;5;6].
(* = [remove nat 1; remove nat 2; remove nat 3; add nat 4; add nat 5; add nat 6]
: list (diff nat) *)
这篇关于如何处理Coq中的Program Fixpoint生成的非常大的术语?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!