简体   繁体   English

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

[英]Coq: Rewriting with 'forall' in hypothesis or goal

I have proved 'correctness' of the reverse function on polymorphic Lists in Coq. 我已经证明了Coq中多态列表的反函数的“正确性”。 The following proof works just fine, but I have a few questions about how the rewrite tactic works. 以下证明工作正常,但我有一些关于重写策略如何工作的问题。

Here's the code: 这是代码:

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.

In the inductive step of the proof for rev_acc_correct if I skip intro k , then rewriting with app_assoc complains that it cannot find a matching subterm. rev_acc_correct的证明的归纳步骤中,如果我跳过介绍k ,那么用app_assoc重写抱怨它无法找到匹配的子项。

Found no subterm matching "(?M1058 ++ ?M1059) ++ ?M1060" in the current goal.

Here, I presume that the ? 在这里,我认为是 before the placeholder names denote that the terms are constrained, in this case to be of type List T for some type T ; 在占位符名称之前表示术语是约束的,在这种情况下,对于某些类型T ,它是类型为List T的类型; and since rev l' and [h] in the goal are instances of List T , one would expect a match in the goal. 并且由于目标中的rev l'[h]List T的实例,因此可以预期目标中的匹配。

On the other hand, rewriting with inductive hypothesis( rewrite -> IHl' ) instead of app_assoc goes through without needing an intro k before. 另一方面,使用归纳假设( 重写 - > IHl' )而不是app_assoc进行重写 ,而不需要前面的介绍k

I find this behaviour of rewrite a bit confusing and the Coq manual doesn't provide any details. 我发现这种重写行为有点令人困惑,Coq手册没有提供任何细节。 I don't want to have to read through the implementation but I need a good operational understanding of what the rewrite tactic does, especially with regards to how term matching works. 我不想阅读实现,但我需要对重写策略的作用有一个良好的操作理解,特别是关于术语匹配如何工作。 Any answers/references in this direction are highly appreciated. 任何在这个方向的答案/参考都非常感谢。

The complication with this rewrite is that there's a binder (the forall k ), which can complicate things. 这种重写的复杂性在于有一个绑定器forall k ),它可以使事情变得复杂。 If you just want things to work, use setoid_rewrite instead of rewrite and it will rewrite under binders. 如果你只是想要工作,使用setoid_rewrite而不是rewrite ,它将在绑定器下重写。

  • rewrite IHl' looks like it happens under a binder, but the pattern being re-written doesn't actually involve the bound variable, so the binder isn't actually important. rewrite IHl'看起来像是在绑定器下发生,但rewrite IHl'的模式实际上并不涉及绑定变量,因此绑定器实际上并不重要。 Here's what I mean: the goal is 这就是我的意思:目标是

     forall k : list T, (rev l' ++ [h]) ++ k = rev_acc l' (h :: k) 

    which is the same thing as (that is, equal to): 这与(即等于)相同:

      (fun l : list T => forall k : list T, l ++ k = rev_acc l' (h :: k)) (rev l' ++ [h]) 

    which I got using pattern (rev l' ++ [h]) in Ltac. 我在Ltac中使用了pattern (rev l' ++ [h]) Now it's clear that you can just rewrite the part being applied to and ignore the binder. 现在很明显,您可以只重写正在应用的部分并忽略活页夹。 When you do rewrite IHl' Coq easily figures out that IHl should be specialized to [h] and the rewrite proceeds. 当你rewrite IHl'时,很容易发现IHl应该专门用于[h]并且重写继续进行。

  • rewrite app_assoc , on the other hand, needs to be specialized to three lists, specifically rev l' , [h] , and k . 另一方面, rewrite app_assoc需要专门针对三个列表,特别是rev l'[h]k It can't be specialized in the current context because the variable k is only bound underneath the forall . 它不能专门用于当前上下文,因为变量k仅绑定在forall下面。 This is why the pattern (?x ++ ?y) ++ ?z doesn't appear in the goal. 这就是为什么模式(?x ++ ?y) ++ ?z没有出现在目标中。

So what do you actually do? 那你究竟做了什么? You can of course introduce k so there is no binder, but there's a simpler and more general technique: Coq has generalized rewriting that can rewrite under binders, which you can use by instead calling setoid_rewrite (see Rewriting under binders in the Coq reference manual). 你当然可以介绍k所以没有绑定器,但有一种更简单,更通用的技术:Coq具有可以在绑定器下重写的通用重写,你可以使用它来代替调用setoid_rewrite (参见Coq参考手册中的绑定下重写 ) 。 The manual tells you you need to declare morphisms, but the relevant ones have all been implemented for you in this case for forall , so setoid_rewrite app_assoc will just work. 手册告诉你需要声明态射,但是在这种情况下相关的已经为你实现了forall ,所以setoid_rewrite app_assoc将正常工作。

Note that while you can always introduce a forall to get rid of the binder, setoid_rewrite can be really handy when your goal is an exists . 请注意,虽然你总是可以引入一个forall来摆脱绑定, setoid_rewrite当你的目标exists时, setoid_rewrite可以非常方便。 Rather than using eexists you can just rewrite under the binder. 您可以在绑定器下重写,而不是使用eexists

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM