合并匹配 Coq 中的重复案例
Merge duplicate cases in match Coq
这个问题我已经遇到过很多次了:我在 Coq 中有一个证明状态,它包括相等两边相同的匹配项。
有没有一种标准的方法可以将多个匹配重写为一个?
例如
match expression_evaling_to_Z with
Zarith.Z0 => something
Zartih.Pos _ => something_else
Zarith.Neg _ => something_else
end = yet_another_thing.
如果我在 expresion_evaling_to_Z
上破坏,我会得到两个相同的目标。我想找到一种方法只获得一个目标。
你可以把match
表达式写得更简洁:
match expression_evaling_to_Z with
| Z0 => something
| Zpos _ | Zneg _ => something_else
end = yet_another_thing.
但是在使用 destruct
时会给你 3 个子目标。
在这种特殊情况下,我们可能会使用这样一个事实,即您实际上需要区分零和非零情况,这看起来像是 Z.abs_nat : Z -> nat
函数的工作。
Require Import Coq.ZArith.BinIntDef.
match Z.abs_nat (expression_evaling_to_Z) with
| O => something
| S _ => something_else
end = yet_another_thing.
这只会让你得到两个子案例,但你需要在 Z.abs_nat (expression_evaling_to_Z)
上进行析构或引入一个新变量。如果您选择第一个变体,那么您可能需要 destruct (...) eqn:Heq.
将等式放入上下文中。
基本上,这种方法是关于寻找新的数据类型(或定义一个)和一个合适的函数来从旧类型映射到新类型。
标准解决方案是使用类型族定义数据类型的 "a view" ,该类型族在析构时会引入适当的条件和情况。对于您的特定情况,您可以这样做:
Require Import Coq.ZArith.ZArith.
Inductive zero_view_spec : Z -> Type :=
| Z_zero : zero_view_spec Z0
| Z_zeroN : forall z, z <> Z0 -> zero_view_spec z.
Lemma zero_viewP z : zero_view_spec z.
Proof. now destruct z; [constructor|constructor 2|constructor 2]. Qed.
Lemma U z : match z with
Z0 => 0
| Zpos _ | Zneg _ => 1
end = 0.
Proof.
destruct (zero_viewP z).
Abort.
这是 math-comp 等库中的常见用法,它为实例化类型族的 z
参数提供特殊支持。
如果您不介意打字,您可以使用 replace
将 RHS 替换为您目标的 LHS,这使得解决起来变得微不足道,然后您只需证明一次重写是确实可以。
Open Scope Z.
Lemma L a b :
match a + b with
Z0 => a + b
| Zpos _ => b + a
| Zneg _ => b + a
end = a + b.
replace (b+a) with (a+b). (* 1. replace the RHS with something trivially true *)
destruct (a+b); auto. (* 2. solve the branches in one fell swoop *)
apply Z.add_comm. (* 3. solve only once what is required for the two brances *)
Qed.
也许您可以使用一些 Ltac-fu 或其他引理来不必手动输入 RHS。
这个问题我已经遇到过很多次了:我在 Coq 中有一个证明状态,它包括相等两边相同的匹配项。
有没有一种标准的方法可以将多个匹配重写为一个?
例如
match expression_evaling_to_Z with
Zarith.Z0 => something
Zartih.Pos _ => something_else
Zarith.Neg _ => something_else
end = yet_another_thing.
如果我在 expresion_evaling_to_Z
上破坏,我会得到两个相同的目标。我想找到一种方法只获得一个目标。
你可以把match
表达式写得更简洁:
match expression_evaling_to_Z with
| Z0 => something
| Zpos _ | Zneg _ => something_else
end = yet_another_thing.
但是在使用 destruct
时会给你 3 个子目标。
在这种特殊情况下,我们可能会使用这样一个事实,即您实际上需要区分零和非零情况,这看起来像是 Z.abs_nat : Z -> nat
函数的工作。
Require Import Coq.ZArith.BinIntDef.
match Z.abs_nat (expression_evaling_to_Z) with
| O => something
| S _ => something_else
end = yet_another_thing.
这只会让你得到两个子案例,但你需要在 Z.abs_nat (expression_evaling_to_Z)
上进行析构或引入一个新变量。如果您选择第一个变体,那么您可能需要 destruct (...) eqn:Heq.
将等式放入上下文中。
基本上,这种方法是关于寻找新的数据类型(或定义一个)和一个合适的函数来从旧类型映射到新类型。
标准解决方案是使用类型族定义数据类型的 "a view" ,该类型族在析构时会引入适当的条件和情况。对于您的特定情况,您可以这样做:
Require Import Coq.ZArith.ZArith.
Inductive zero_view_spec : Z -> Type :=
| Z_zero : zero_view_spec Z0
| Z_zeroN : forall z, z <> Z0 -> zero_view_spec z.
Lemma zero_viewP z : zero_view_spec z.
Proof. now destruct z; [constructor|constructor 2|constructor 2]. Qed.
Lemma U z : match z with
Z0 => 0
| Zpos _ | Zneg _ => 1
end = 0.
Proof.
destruct (zero_viewP z).
Abort.
这是 math-comp 等库中的常见用法,它为实例化类型族的 z
参数提供特殊支持。
如果您不介意打字,您可以使用 replace
将 RHS 替换为您目标的 LHS,这使得解决起来变得微不足道,然后您只需证明一次重写是确实可以。
Open Scope Z.
Lemma L a b :
match a + b with
Z0 => a + b
| Zpos _ => b + a
| Zneg _ => b + a
end = a + b.
replace (b+a) with (a+b). (* 1. replace the RHS with something trivially true *)
destruct (a+b); auto. (* 2. solve the branches in one fell swoop *)
apply Z.add_comm. (* 3. solve only once what is required for the two brances *)
Qed.
也许您可以使用一些 Ltac-fu 或其他引理来不必手动输入 RHS。