Coq:用'重写;forall';在假设或目标中

Coq:用'重写;forall';在假设或目标中,coq,coq-tactic,Coq,Coq Tactic,我已经在Coq中证明了多态列表上反向函数的“正确性”。下面的证明很好,但我有几个关于重写策略如何工作的问题 代码如下: Require Export Coq.Lists.List. Import ListNotations. Fixpoint rev {T:Type} (l:list T) : list T := match l with | nil => nil | h :: t => rev t ++ [h] end. (* Prove rev_acc

我已经在Coq中证明了多态列表上反向函数的“正确性”。下面的证明很好,但我有几个关于重写策略如何工作的问题

代码如下:

Require Export Coq.Lists.List.
Import ListNotations.

Fixpoint rev {T:Type} (l:list T) : list T :=
  match l with
  | nil    => nil
  | h :: t => rev t ++ [h]
  end.

(* Prove rev_acc equal to above naive implementation. *)
Fixpoint rev_acc {T:Type} (l acc:list T) : list T :=
  match l with
  | nil => acc
  | h :: t => rev_acc t (h::acc)
  end.

Theorem app_assoc : forall  (T:Type) (l1 l2 l3 : list T),
  (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
Admitted.

Theorem rev_acc_correct : forall (T:Type) (l k :list T),
  rev l ++ k = rev_acc l k.
Proof.
  intros T l.
  induction l as [ | h l' IHl' ].
  - reflexivity.
  - simpl. 
    intro k.
    (* Why is "intro k" required for "rewrite -> app_assoc" *)
    (* But "rewrite -> IHl'" works regardless of "intro k".  *)
    (* generalize (rev l'), [h], k. *)
    rewrite -> app_assoc.
    simpl.
    rewrite -> IHl'.
    reflexivity.
Qed.
在rev_acc_correct证明的归纳步骤中,如果我跳过介绍k,那么使用app_assoc重写会抱怨找不到匹配的子项

Found no subterm matching "(?M1058 ++ ?M1059) ++ ?M1060" in the current goal.
在这里,我想?在占位符名称之前,表示术语受到约束,在这种情况下,对于某些类型T,为列表T类型;由于目标中的rev l'和[h]是列表T的实例,因此我们可以预期目标中会出现匹配

另一方面,用归纳假设(rewrite->IHl')而不是app_assoc进行重写,之前不需要介绍


我发现这种重写行为有点令人困惑,Coq手册没有提供任何细节。我不想通读实现,但我需要对重写策略的功能有一个良好的操作理解,特别是关于术语匹配的工作原理。这方面的任何答案/参考都将受到高度赞赏。

重写的复杂性在于有一个活页夹(所有k的
),它会使事情变得复杂。如果您只是想让事情正常运行,请使用
setoid\u rewrite
而不是
rewrite
,它将在活页夹下重写

  • rewrite IHl'
    看起来像是在活页夹下发生的,但是重新编写的模式实际上并不涉及绑定变量,因此活页夹实际上并不重要。我的意思是:目标是

    forall k : list T, (rev l' ++ [h]) ++ k = rev_acc l' (h :: k)
    
    这与(即等于)相同:

    我使用Ltac中的
    模式(rev l'+[h])
    得到了它。现在很清楚,您可以重写应用到的零件,而忽略活页夹。当您执行
    重写IHl'
    时,Coq很容易发现
    IHl
    应该专门用于
    [h]
    ,然后重写继续进行

  • 另一方面,
    rewrite app_assoc
    需要专用于三个列表,特别是
    rev l'
    [h]
    k
    。无法在当前上下文中对其进行专门化,因为变量
    k
    仅绑定在
    forall
    下面。这就是为什么模式
    (?x++?y++?z
    没有出现在目标中的原因
那么你实际上是做什么的呢?当然,您可以引入
k
,这样就没有活页夹了,但有一种更简单、更通用的技术:Coq有一个通用的重写,可以在活页夹下重写,您可以通过调用
setoid\u rewrite
(参见Coq参考手册中的内容)来使用它。手册告诉您需要声明态射,但在本例中,所有相关的态射都已为您在
forall
中实现,因此
setoid\u rewrite app\u assoc
将正常工作


请注意,虽然您总是可以为所有
引入一个
来摆脱活页夹,但当您的目标是一个
存在时,
setoid\u rewrite
非常方便。您可以在活页夹下重写,而不用
eexists

您的问题很有趣!但是,既然它提供了一个软件基础练习的答案,你能删除它并发布一个没有提到这个练习的最低版本吗?我个人认为这个例子是从任何特定的SF练习中抽象出来的,细节对于揭示这些复杂情况很重要,这个问题比隐藏这个特定的解决方案更有价值。也许您可以删除问题本身中对软件基础的引用?据我所知,上述代码中练习的唯一解决方案是实现。我已经尽我所能将其最小化,同时仍然保持证据的上下文。如果需要,请随时编辑。
 (fun l : list T => forall k : list T, l ++ k = rev_acc l' (h :: k)) (rev l' ++ [h])