我正在尝试将每个整数形式化为自然数对的等价类,其中第一个成分是正数部分,第二个成分是负数部分。
Definition integer : Type := prod nat nat.
我想定义一个归一化函数,使正负尽可能多地抵消。
Fixpoint normalize (i : integer) : integer :=
let (a, b) := i in
match a with
| 0 => (0, b)
| S a' => match b with
| 0 => (S a', 0)
| S b' => normalize (a', b')
end
end.
但是Coq说:
错误: 归一化的递归定义格式不正确。 在环境中 标准化:整数->整数 i:整数 a:自然 b:自然 a':自然 b':自然 递归调用以规范化为主要参数 等于“(a',b')”,而不是“ i”的子项。
我认为这可能与有充分根据的递归有关?
答案 0 :(得分:6)
现在Program Fixpoint
变得如此出色,您可以像这样定义normalize
:
Require Import Program.
Definition integer :Type := (nat*nat).
Program Fixpoint normalize (i:integer) {measure (max (fst i) (snd i))} :=
match i with
| (S i1, S i2) => normalize (i1, i2)
| (_, _) => i
end.
它能够自行处理所有举证责任!
要使用它并对此进行推理,您可能需要定义一些重写引理。
Lemma normalize_0_l i: normalize (0, i) = (0, i).
Proof. reflexivity. Qed.
Lemma normalize_0_r i: normalize (i, 0) = (i, 0).
Proof. destruct i; reflexivity. Qed.
Lemma normalize_inj i j: normalize (S i, S j) = normalize (i, j).
unfold normalize at 1; rewrite fix_sub_eq; simpl; fold (normalize (i, j)).
- reflexivity.
- now intros [[|x] [|y]] f g H.
Qed.
我从here获得了unfold... rewrite ... simpl... fold
技术!
答案 1 :(得分:6)
除了@larsr的回答:Equations插件还提供了一些不错的功能,例如自动生成类似于normalize_0_l
的简化引理等。对于下面的示例,我们有normalize_equation_1
,normalize_equation_2
等。
而且,就像Function
插件一样,Equations
提供了功能归纳方案,使有关函数属性的证明非常优雅。
From Equations Require Import Equations.
Definition integer : Type := prod nat nat.
Equations normalize (i : integer) : integer by wf (fst i) :=
normalize (0, b) := (0, b);
normalize (S a', 0) := (S a', 0);
normalize (S a', S b') := normalize (a', b')
.
(* see Coq's response for the list of auto generated lemmas *)
让我们使用功能归纳证明normalize
的某些属性。公式提供了一些使使用起来更容易的策略。在这种情况下,我将使用funelim
。
From Coq Require Import Arith.
Lemma normalize_sub_lt a b :
a < b -> normalize (a, b) = (0, b - a).
Proof.
funelim (normalize (a, b)); simpl in *.
- now rewrite Nat.sub_0_r.
- now intros []%Nat.nlt_0_r.
- intros a_lt_b%Nat.succ_lt_mono; auto.
Qed.
normalize
规范的第二部分可以用相同的方式证明。
Lemma normalize_sub_gte a b :
b <= a -> normalize (a, b) = (a - b, 0).
答案 2 :(得分:5)
必须在原始参数的“子项”上进行递归调用。归纳类型的术语的子术语本质上是与用于创建原始术语的类型相同的术语。例如,像S a'
这样的自然数的子项是a'
。
不幸的是,对于您的定义(如所写),一对i: prod nat nat
在此意义上没有任何子术语。这是因为prod
不是递归类型。它的构造函数pair: A -> B -> prod A B
不接受任何类型prod A B
的参数。
要解决此问题,建议您先在两个单独的自然数上定义函数。
Fixpoint normalize_helper (a b : nat) : integer :=
match a with
| 0 => (0, b)
| S a' => match b with
| 0 => (S a', 0)
| S b' => normalize a' b'
end
end.
然后可以很容易地根据normalize
来定义normalize_helper
。
答案 3 :(得分:3)
虽然学习如何编写此类递归函数很有用,但在这种特殊情况下,我认为最好避免递归并使用标准定义:
Require Import Coq.Arith.Arith.
Definition integer : Type := (nat * nat).
Definition normalize (i : integer) : integer :=
if snd i <=? fst i then (fst i - snd i, 0)
else (0, snd i - fst i).