如何在 Coq 中使用归纳类型来处理案例
我想使用destruct
策略通过案例来证明一个陈述。我在网上读了几个例子,但我很困惑。有人可以更好地解释一下吗?
这是一个小例子(还有其他方法可以解决它,但尝试使用 destruct
):
Inductive three := zero
| one
| two.
Lemma has2b2: forall a:three, a<>zero /\ a<>one -> a=two.
现在一些在线示例建议执行以下操作:
intros. destruct a.
在这种情况下我得到:
3 subgoals H : zero <> zero /\ zero <> one
______________________________________(1/3)
zero = two
______________________________________(2/3)
one = two
______________________________________(3/3)
two = two
所以,我想证明第一个两种情况是不可能的。但机器将它们列为子目标并希望我证明它们......这是不可能的。
概括: 如何准确剔除不可能的情况?
我看过一些使用 inversion
的例子,但我不明白这个过程。
最后,如果我的引理取决于几种归纳类型并且我仍然想涵盖所有情况,会发生什么?
I wan to use the destruct
tactic to prove a statement by cases. I have read a couple of examples online and I'm confused. Could someone explain it better?
Here is a small example (there are other ways to solve it but try using destruct
):
Inductive three := zero
| one
| two.
Lemma has2b2: forall a:three, a<>zero /\ a<>one -> a=two.
Now some examples online suggest doing the following:
intros. destruct a.
In which case I get:
3 subgoals H : zero <> zero /\ zero <> one
______________________________________(1/3)
zero = two
______________________________________(2/3)
one = two
______________________________________(3/3)
two = two
So, I want to prove that the first two cases are impossible. But the machine lists them as subgoals and wants me to PROVE them... which is impossible.
Summary:
How to exactly discard the impossible cases?
I have seen some examples using inversion
but I don't understand the procedure.
Finally, what happens if my lemma depends on several inductive types and I still want to cover ALL cases?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如何排除不可能的情况?嗯,确实,前两项义务无法证明,但请注意,它们具有相互矛盾的假设(分别是
零<>零
和一<>一
)。因此,您将能够使用 tauto 来证明这些目标(如果您感兴趣,还有更原始的策略可以实现这一点)。inversion
是 destruct 的更高级版本。除了“破坏”归纳法之外,它有时还会生成一些等式(您可能需要)。它本身是归纳
的简单版本,它还会为您生成归纳假设。如果您的目标中有多种归纳类型,您可以将它们一一
解构/反转
。更详细的演练:
How to discard impossible cases? Well, it's true that the first two obligations are impossible to prove, but note they have contradicting assumptions (
zero <> zero
andone <> one
, respectively). So you will be able to prove those goals withtauto
(there are also more primitive tactics that will do the trick, if you are interested).inversion
is a more advanced version of destruct. Additional to 'destructing' the inductive, it will sometimes generate some equalities (that you may need). It itself is a simple version ofinduction
, which will additionally generate an induction hypothesis for you.If you have several inductive types in your goal, you can
destruct/invert
them one by one.More detailed walk-through:
如果您看到一个不可能的目标,则有两种可能性:要么您在证明策略中犯了错误(也许您的引理是错误的),要么假设是矛盾的。
如果您认为这些假设是矛盾的,您可以将目标设置为
False
,以消除一点复杂性。elimtype False
实现了这一点。通常,您通过证明命题P
及其否定~P
来证明False
;策略荒谬的P
从P
和~P
推断出任何目标。如果存在一个矛盾的特定假设,contradict H
会将目标设置为~H
,或者如果假设是否定的~A
,则目标将是A
(比~~A
更强,但通常更方便)。如果一个特定的假设明显矛盾,矛盾 H
或只是矛盾
将证明任何目标。有许多策略涉及归纳类型的假设。确定使用哪一个主要取决于经验。以下是主要的(但您很快就会遇到这里未涵盖的情况):
destruct
只是将假设分解为几个部分。它丢失了有关依赖关系和递归的信息。一个典型的例子是destruct H
,其中H
是一个连词H : A /\ B
,它将H
拆分为A
和B
类型的两个独立假设;或双重destruct H
,其中H
是析取H : A \/ B
,它将证明分成两个不同的子证明,一个带有假设A
和假设B
的一个。case_eq
与destruct
类似,但保留了假设与其他假设的联系。例如,destruct n
,其中n : nat
将证明分解为两个子证明,一个用于n = 0
,另一个用于n = S米。如果
n
用于其他假设(即您有一个H : P n
),您可能需要记住您已经得到的n
destructed 与这些假设中使用的n
相同:case_eq n
执行此操作。inversion
对假设类型进行案例分析。当destruct
会忘记的假设类型存在依赖关系时,它特别有用。您通常会对Set
中的假设(其中相等相关)使用case_eq
,对Prop
中的假设使用inversion
(其中有非常依赖的类型)。inversion
策略留下了很多等式,并且通常后面跟着subst
来简化假设。inversion_clear
策略是inversion 的简单替代方案; subst
但丢失了一些信息。归纳
意味着您将通过给定假设的归纳(=递归)来证明目标。例如,归纳 n
,其中n : nat
表示您将执行整数归纳并证明基本情况(n
替换为0
)和归纳情况(n
替换为m+1
)。你的例子很简单,你可以证明它“通过对
a
的案例分析是显而易见的”。但是让我们看看由
destruct
策略生成的情况,即在intros; 之后。销毁a。
。 (a
为one
的情况是对称的;最后一种情况,其中a
为two
,很明显:反身性。)这个目标看起来不可能。我们可以将这一点告诉 Coq,在这里它可以自动发现矛盾(
zero=zero
是显而易见的,其余的是由tauto
策略处理的一阶同义反复) )。事实上,即使你不一开始就告诉 Coq 不要担心目标,并且先编写了不带
elimtype False
的tauto
,tauto
仍然可以工作( IIRC 它在旧版本的 Coq 中没有)。您可以通过编写info tauto
来了解 Coq 使用tauto
策略所做的事情。 Coq 会告诉您 tauto 策略生成了什么证明脚本。这不太容易理解,所以让我们看一下这个案例的手动证明。首先,让我们将假设(这是一个合取)分成两部分。我们现在有两个假设,其中之一是
零<>。零。这显然是错误的,因为它是
zero = Zero
的否定,而这显然是正确的。我们可以更详细地了解
矛盾
策略的作用。 (信息矛盾
会揭示场景下发生的情况,但同样这对新手不友好)。我们声称目标是正确的,因为假设是矛盾的,所以我们可以证明任何事情。因此,让我们将中间目标设置为False
。在 H0 中运行“red”。 可以看到“0
”零
实际上是~(zero=zero)
的表示法,它又被定义为含义zero=zero ->错误。所以
False
是H0
的结论:现在我们需要证明
zero=zero
,即现在我们已经证明了我们的断言
错误
。剩下的就是证明False
暗示了我们的目标。好吧,False
意味着任何目标,这就是它的定义(False
被定义为具有 0 个案例的归纳类型)。If you see an impossible goal, there are two possibilities: either you made a mistake in your proof strategy (perhaps your lemma is wrong), or the hypotheses are contradictory.
If you think the hypotheses are contradictory, you can set the goal to
False
, to get a little complexity out of the way.elimtype False
achieves this. Often, you proveFalse
by proving a propositionP
and its negation~P
; the tacticabsurd P
deduces any goal fromP
and~P
. If there's a particular hypothesis which is contradictory,contradict H
will set the goal to~H
, or if the hypothesis is a negation~A
then the goal will beA
(stronger than~ ~A
but usually more convenient). If one particular hypothesis is obviously contradictory,contradiction H
or justcontradiction
will prove any goal.There are many tactics involving hypotheses of inductive types. Figuring out which one to use is mostly a matter of experience. Here are the main ones (but you will run into cases not covered here soon):
destruct
simply breaks down the hypothesis into several parts. It loses information about dependencies and recursion. A typical example isdestruct H
whereH
is a conjunctionH : A /\ B
, which splitsH
into two independent hypotheses of typesA
andB
; or duallydestruct H
whereH
is a disjunctionH : A \/ B
, which splits the proof into two different subproofs, one with the hypothesisA
and one with the hypothesisB
.case_eq
is similar todestruct
, but retains the connections that the hypothesis has with other hypotheses. For example,destruct n
wheren : nat
breaks the proof into two subproofs, one forn = 0
and one forn = S m
. Ifn
is used in other hypotheses (i.e. you have aH : P n
), you may need to remember that then
you've destructed is the samen
used in these hypotheses:case_eq n
does this.inversion
performs a case analysis on the type of a hypothesis. It is particularly useful when there are dependencies in the type of the hypothesis thatdestruct
would forget. You would typically usecase_eq
on hypotheses inSet
(where equality is relevant) andinversion
on hypotheses inProp
(which have very dependent types). Theinversion
tactic leaves a lot of equalities behind, and it's often followed bysubst
to simplify the hypotheses. Theinversion_clear
tactic is a simple alternative toinversion; subst
but loses a little information.induction
means that you are going to prove the goal by induction (= recursion) on the given hypothesis. For example,induction n
wheren : nat
means that you'll perform integer induction and prove the base case (n
replaced by0
) and the inductive case (n
replaced bym+1
).Your example is simple enough that you can prove it as “obvious by case analysis on
a
”.But let's look at the cases generated by the
destruct
tactic, i.e. after justintros; destruct a.
. (The case wherea
isone
is symmetric; the last case, wherea
istwo
, is obvious by reflexivity.)The goal looks impossible. We can tell this to Coq, and here it can spot the contradiction automatically (
zero=zero
is obvious, and the rest is a first-order tautology handled by thetauto
tactic).In fact
tauto
works even if you don't start by telling Coq not to worry about the goal and wrotetauto
without theelimtype False
first (IIRC it didn't in older versions of Coq). You can see what Coq is doing with thetauto
tactic by writinginfo tauto
. Coq will tell you what proof script thetauto
tactic generated. It's not very easy to follow, so let's look at a manual proof of this case. First, let's split the hypothesis (which is a conjunction) into two.We now have two hypotheses, one of which is
zero <> zero
. This is clearly false, because it's the negation ofzero = zero
which is clearly true.We can look in even more detail at what the
contradiction
tactic does. (info contradiction
would reveal what happens under the scene, but again it's not novice-friendly). We claim that the goal is true because the hypotheses are contradictory so we can prove anything. So let's set the intermediate goal toFalse
.Run
red in H0.
to see thatzero <> zero
is really notation for~(zero=zero)
which in turn is defined as meaningzero=zero -> False
. SoFalse
is the conclusion ofH0
:And now we need to prove that
zero=zero
, which isNow we've proved our assertion of
False
. What remains is to prove thatFalse
implies our goal. Well,False
implies any goal, that's its definition (False
is defined as an inductive type with 0 case).