tac1 ; tac2 的语义是运行tac1,然后在tac1 创建的所有子目标上运行tac2。所以你可能会面临各种各样的情况:
跑完tac1没有目标了
如果在运行tac1 之后没有剩余目标,那么tac2 永远不会运行,Coq 只是默默地成功。例如,在第一个推导中,我们在(有效)证明的末尾有一个无用的; intros:
Goal forall (A : Prop), A -> (A /\ A /\ A /\ A /\ A).
intros ; repeat split ; assumption ; intros.
Qed.
如果我们隔离它,那么我们会得到一个Error: No such goal.,因为我们正在尝试在没有什么可以证明的情况下运行策略!
Goal forall (A : Prop), A -> (A /\ A /\ A /\ A /\ A).
intros ; repeat split ; assumption.
intros. (* Error! *)
跑完tac1,只剩下一个目标了。
如果在运行tac1 之后只剩下一个目标,那么tac1 ; tac2 的行为有点像tac1. tac2。主要区别在于,如果tac2 失败,那么整个tac1 ; tac2 也会失败,因为这两种策略的顺序被视为一个单元,可以作为一个整体成功,也可以作为一个整体失败。但是如果tac2 成功了,那就差不多了。
例如以下证明是有效的:
Goal forall (A : Prop), A -> (A /\ A /\ A /\ A /\ A).
intros.
repeat split ; assumption.
Qed.
运行tac1 会产生多个目标。
最后,如果通过运行tac1 生成多个目标,那么tac2 将应用于所有生成的子目标。在我们的运行示例中,我们可以观察到,如果我们在repeat split 之后切断战术序列,那么我们手头有 5 个目标。这意味着我们需要复制/粘贴assumption 5 次才能复制前面使用; 给出的证明:
Goal forall (A : Prop), A -> (A /\ A /\ A /\ A /\ A).
intros ; repeat split.
assumption.
assumption.
assumption.
assumption.
assumption.
Qed.