Coq添加新变量而不是使用正确的变量
我正在研究我自己在Coq中的向量实现,我遇到了一个奇怪的问题 以下是我迄今为止的代码:Coq添加新变量而不是使用正确的变量,coq,Coq,我正在研究我自己在Coq中的向量实现,我遇到了一个奇怪的问题 以下是我迄今为止的代码: Inductive Fin : nat -> Type := |FZ : forall n, Fin (S n) |FS : forall n, Fin n -> Fin (S n). Definition emptyf(A : Type) : Fin 0 -> A. intro e; inversion e. Defined. Inductive Vec(A : Type)
Inductive Fin : nat -> Type :=
|FZ : forall n, Fin (S n)
|FS : forall n, Fin n -> Fin (S n).
Definition emptyf(A : Type) : Fin 0 -> A.
intro e; inversion e.
Defined.
Inductive Vec(A : Type) : nat -> Type :=
|Nil : Vec A 0
|Cons : forall n, A -> Vec A n -> Vec A (S n).
Definition head(A : Type)(n : nat)(v : Vec A (S n)) : A :=
match v with
|Cons a _ => a
end.
Definition tail(A : Type)(n : nat)(v : Vec A (S n)) : Vec A n :=
match v with
|Cons _ w => w
end.
Fixpoint index(A : Type)(n : nat) : Vec A n -> Fin n -> A :=
match n as n return Vec A n -> Fin n -> A with
|0 => fun _ i => emptyf _ i
|S m => fun v i => match i with
|FZ _ => head v
|FS j => index (tail v) j
end
end.
直到tail
的所有内容都可以很好地编译,但是当我尝试编译索引时,我收到以下错误:
Error:
In environment
index : forall (A : Type) (n : nat), Vec A n -> Fin n -> A
A : Type
n : nat
m : nat
v : Vec A (S m)
i : Fin (S m)
n0 : nat
j : Fin n0
The term "j" has type "Fin n0" while it is expected to have type "Fin m".
显然,罪魁祸首是Coq引入了新变量n0
,而不是分配j
类型Fin m
,即使这是j
唯一可能的类型,这将导致i
从j
构建。你知道为什么会发生这种情况,以及我怎样才能解决这个问题吗?我发现Vec
和Fin
通常很难使用,所以最近我使用了math comp中的'I_n
和n.-tuples T
,它们只是自然的和附有不相关证明的列表。但是,如果您想继续享受复杂模式匹配的乐趣,可以尝试定义更强大的模式匹配原则:
Definition fin_case T m (i : Fin m) : T -> (Fin (pred m) -> T) -> T :=
match i with
| FZ _ => fun fn fz => fn
| FS _ j => fun fn fz => fz j
end.
一旦有了fin_case
,函数定义就可以工作了:
Fixpoint index (A : Type) (n : nat) : Vec A n -> Fin n -> A :=
match n as n return Vec A n -> Fin n -> A with
| 0 => fun _ i => emptyf _ i
| S m => fun v i => fin_case i (head v) (index (tail v))
end.
我发现一般来说很难使用Vec
和Fin
,所以这些天我使用了math comp中的'I_n
和n.-tuples T
,它们只是自然的和附带了不相关证明的列表。但是,如果您想继续享受复杂模式匹配的乐趣,可以尝试定义更强大的模式匹配原则:
Definition fin_case T m (i : Fin m) : T -> (Fin (pred m) -> T) -> T :=
match i with
| FZ _ => fun fn fz => fn
| FS _ j => fun fn fz => fz j
end.
一旦有了fin_case
,函数定义就可以工作了:
Fixpoint index (A : Type) (n : nat) : Vec A n -> Fin n -> A :=
match n as n return Vec A n -> Fin n -> A with
| 0 => fun _ i => emptyf _ i
| S m => fun v i => fin_case i (head v) (index (tail v))
end.
使用match
时,可能会丢失信息。我用这个方法把信息放回上下文中
match i in Fin (S n0) return n0 = m -> A with
... => fun H : n0 = m => ...
end eq_refl
使Coq能够在上下文中获取信息n0=m
。它作为函数参数发送到match子句中。要在类型检查中使用它,我使用(将H与…end匹配)
,以便Coq了解Fin n0=Fin m
。
这就是解决办法
Fixpoint index(A : Type)(n : nat) : Vec A n -> Fin n -> A :=
match n as n return Vec A n -> Fin n -> A with
|0 => fun _ i => emptyf _ i
|S m => fun v i =>
match i in Fin (S n') return n' = m -> A with
|FZ _ => fun _ => head _ _ v
|FS _ j => fun H => index (tail v) (match H with eq_refl _ => j end)
end eq_refl
end.
当类型检查不理解两件事是相等的时,通常护航模式可以帮助您将该信息放入上下文中。我还建议使用refine
逐步建立术语。它可以让您查看上下文中有哪些信息。当您使用match
时,可能会丢失信息。我用这个方法把信息放回上下文中
match i in Fin (S n0) return n0 = m -> A with
... => fun H : n0 = m => ...
end eq_refl
使Coq能够在上下文中获取信息n0=m
。它作为函数参数发送到match子句中。要在类型检查中使用它,我使用(将H与…end匹配)
,以便Coq了解Fin n0=Fin m
。
这就是解决办法
Fixpoint index(A : Type)(n : nat) : Vec A n -> Fin n -> A :=
match n as n return Vec A n -> Fin n -> A with
|0 => fun _ i => emptyf _ i
|S m => fun v i =>
match i in Fin (S n') return n' = m -> A with
|FZ _ => fun _ => head _ _ v
|FS _ j => fun H => index (tail v) (match H with eq_refl _ => j end)
end eq_refl
end.
当类型检查不理解两件事是相等的时,通常护航模式可以帮助您将该信息放入上下文中。我还建议使用refine
逐步建立术语。它让您看到上下文中有哪些信息。注意,您不需要对n
进行模式匹配,而只需要对Fin n
类型的参数进行模式匹配。由此产生的定义更简单
Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
match i in Fin n0 return Vec A n0 -> A with
| FZ => fun v => head v
| FS j => fun v => index j (tail v)
end.
Coq实际上足够精确,可以猜测注释
Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
match i with
| FZ => fun v => head v
| FS j => fun v => index j (tail v)
end.
请注意,您不需要对n
进行模式匹配,而只需要对类型为Fin n
的参数进行模式匹配。由此产生的定义更简单
Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
match i in Fin n0 return Vec A n0 -> A with
| FZ => fun v => head v
| FS j => fun v => index j (tail v)
end.
Coq实际上足够精确,可以猜测注释
Fixpoint index {A:Type} {n:nat} (i:Fin n) : Vec A n -> A :=
match i with
| FZ => fun v => head v
| FS j => fun v => index j (tail v)
end.
除了其他答案之外,还有一个基于策略的解决方案:
Fixpoint index (A : Type) (n : nat) (v : Vec A n) (i : Fin n) : A.
destruct v as [| n h tl].
- exact (emptyf A i).
- inversion i as [ | ? i'].
+ exact h.
+ exact (index _ _ tl i').
Defined.
inversion
策略负责“信息丢失”。如果您尝试打印索引。
结果不会很好,但Coq基本上使用了@larsr提到的护航模式
请注意,这种方法不在
n
上使用模式匹配。相反,它的模式与向量参数匹配,这就是为什么它不需要head
和tail
函数的原因。只是为了补充其他答案,一个基于策略的解决方案:
Fixpoint index (A : Type) (n : nat) (v : Vec A n) (i : Fin n) : A.
destruct v as [| n h tl].
- exact (emptyf A i).
- inversion i as [ | ? i'].
+ exact h.
+ exact (index _ _ tl i').
Defined.
inversion
策略负责“信息丢失”。如果您尝试打印索引。结果不会很好,但Coq基本上使用了@larsr提到的护航模式
请注意,这种方法不在
n
上使用模式匹配。相反,它的模式与向量参数匹配,这就是为什么它不需要head
和tail
函数的原因。谢谢你的回答,但在我看来,这只是把问题推到了如何定义fin_case
上。不管我怎么做,我还是会遇到同样的问题。你介意分享一下你心中的定义吗?更新后,有几条评论:a)注意,这种“将问题推上”的方式确实不止于此,而且是Coq风格相关编程中的一个关键工具。b) 解决这类问题还有很多其他方法,事实上,写一个合适的匹配仍然感觉像是一门艺术。谢谢你的回答,但在我看来,这只是把问题推到了如何定义fin\u case
。不管我怎么做,我还是会遇到同样的问题。你介意分享一下你心中的定义吗?更新后,有几条评论:a)注意,这种“将问题推上”的方式确实不止于此,而且是Coq风格相关编程中的一个关键工具。b) 还有很多其他方法可以解决这类问题,事实上,编写一个合适的匹配仍然感觉像是一门艺术。一个简单的定义cast(mn:nat)(i:Fin m)(H:m=n):Fin n:=eq_rec m Fin i n H。
可能会让代码更容易阅读((将H与eq_refl匹配)
被(cast j H)
)。对使用等式转换的解决方案(包括带反转的解决方案)的评论是,它们可能无法在Coq内正确计算/减少,需要额外重写。一个简单的定义转换(mn:nat)(i:Fin m)(H:m=n):Fin n:=eq_rec m Fin i n H.
可能会使代码更易于阅读((将H与eq匹配_