假设我有一个归纳类型的算术表达式exp
Inductive exp : Type :=
| num : nat -> exp
| plus : exp -> exp -> exp
| minus : exp -> exp -> exp
| mult : exp -> exp -> exp
| div : exp -> exp -> exp.
我想定义一个函数expsum
,它返回exp
中出现的所有数字的总和。显而易见的实施是
Fixpoint expsum (e : exp) : nat :=
match e with
| num n => n
| plus e1 e2 => expsum e1 + expsum e2
| minus e1 e2 => expsum e1 + expsum e2
| mult e1 e2 => expsum e1 + expsum e2
| div e1 e2 => expsum e1 + expsum e2
end.
但对于构造函数plus
,minus
,mult
和div
,expsum
在模式匹配后完成相同的操作。我想把它简化为像
Fixpoint expsum (e : exp) : nat :=
match e with
| num n => n
| [ plus e1 e2 | minus e1 e2 | mult e1 e2 | div e1 e2 ]
=> expsum e1 + expsum e2
end.
这样单个子句就可以处理多个构造函数。我想我已经在其他功能语言中看到了这一点。这在Coq中是否可行?
答案 0 :(得分:4)
这不能用术语语言来完成。由于其依赖类型,Coq的语言本身非常强大,但它不是它自己的元语言;没有办法编写Coq术语来操纵Coq构造函数(仅作为术语,并且这不足以构建模式匹配)。
可能有一种方法可以用白话(用词语定义术语,用战术语言证明等等)来做到这一点,但我认为没有。如果它存在于任何地方,我希望它在Program
。但是将相同的模式应用于碰巧具有相同类型的构造函数是一个相当特殊的需求。
可以使用证明语言来完成。 Coq中的证明只是条款;帮助重复性证明的策略可以用重复的术语以同样的方式帮助。
Inductive exp : Type :=
| num : nat -> exp
| plus : exp -> exp -> exp
| minus : exp -> exp -> exp
| mult : exp -> exp -> exp
| div : exp -> exp -> exp.
(* The boring old code *)
Fixpoint expsum (e : exp) : nat :=
match e with
| num n => n
| plus e1 e2 => expsum e1 + expsum e2
| minus e1 e2 => expsum e1 + expsum e2
| mult e1 e2 => expsum e1 + expsum e2
| div e1 e2 => expsum e1 + expsum e2
end.
Definition expsum_tactic : exp -> nat.
induction 1;
(* Figure out the computation automatically based on what arguments are present *)
exact n || exact (IHexp1 + IHexp2).
Defined. (* "Defined" rather than "Qed" to get a transparent definition *)
(* Show the two definitions in a nice way to visually compare them *)
Print expsum.
Eval compute [expsum expsum_tactic exp_rec] in (expsum, expsum_tactic).
通过使用match goal
战术构造来分析每个构造函数的参数并相应地构建结果项,可以将其推广到变量arity。
虽然这有效,但它很棘手。策略适用于编写校样,其中计算内容无关紧要。当您使用它们来编写实际定义很重要的术语(而不仅仅是类型)时,您需要非常小心地确保定义您期望的术语,而不是恰好具有相同的其他术语。类型。你现在可能已经考虑了几分钟,但这段代码并没有赢得可读性奖励。
我一般不推荐这种方法,因为它容易出错。 但是当你有许多相似的类型和功能,并且在开发过程中类型发生变化时,它会很有用。你最终得到了相当难以理解的战术,但是一旦你调试了它们,即使你调整了表达式类型,它们也可以工作。
答案 1 :(得分:2)
这在Coq> = 8.5
中是可能的Fixpoint expsum (e : exp) : nat :=
match e with
| num n => n
| (plus e1 e2 | minus e1 e2 | mult e1 e2 | div e1 e2) => expsum e1 + expsum e2 end.
Print expsum. (*expsum =
fix expsum (e : exp) : nat :=
match e with
| num n => n
| plus e1 e2 => expsum e1 + expsum e2
| minus e1 e2 => expsum e1 + expsum e2
| mult e1 e2 => expsum e1 + expsum e2
| div e1 e2 => expsum e1 + expsum e2
end
: exp -> nat*)