请考虑the standard library中find
的定义,其类型为find: forall A : Type, (A -> bool) -> list A -> option A
。
当然,find
必须返回option A
而不是A
,因为我们不知道列表中是否存在“有效”元素。
现在,说我发现find
的定义很痛苦,因为即使必须确保列表中存在这样的元素,我们也必须处理该选项。
因此,我想定义myFind
,它另外需要证明列表中有这样的元素。就像这样:
Variable A: Type.
Fixpoint myFind
(f: A -> bool)
(l: list A)
(H: exists a, In a l /\ f a = true): A :=
...
如果我没记错的话,这样的签名会非正式地说:“给我一个函数,一个列表以及一个证明您在列表中具有“有效”元素的证据。”
我的问题是:如何使用提供的假设并定义我的固定点?
我的想法是:
match l with
| nil => (* Use H to prove this case is not possible *)
| hd :: tl =>
if f hd
then hd
else
(* Use H and the fact that f hd = false
to prove H': exists a, In a tl /\ f a = true *)
myFind f tl H'
end.
一个额外的好处是知道我是否可以直接在类型中嵌入关于结果的属性,例如,在我们的例子中,证明返回值r
确实是f r = true
的证明。
答案 0 :(得分:2)
我们可以通过对输入列表进行结构递归来实现此myFind
函数。在空列表的情况下,False_rect
归纳原理是我们的朋友,因为它使我们从逻辑世界切换到计算世界。通常,如果正在构建的术语的类型生活在Type
中,我们就不能破坏命题证明,但是如果我们有不一致之处,则系统会允许我们这样做。
我们可以使用 convoy模式(在Stackoverflow上有很多很好的答案解释了这种模式)和辅助引理find_not_head
来处理非空输入列表的情况。
在以下实现中添加两次使用convoy模式可能是有用的:顶层模式用于让Coq知道第一个match
分支中的输入列表为空-观察到H
的类型在两个分支中都是不同的。
From Coq Require Import List.
Import ListNotations.
Set Implicit Arguments.
(* so we can write `f a` instead of `f a = true` *)
Coercion is_true : bool >-> Sortclass.
Section Find.
Variables (A : Type) (f : A -> bool).
(* auxiliary lemma *)
Fact find_not_head h l : f h = false ->
(exists a, In a (h :: l) /\ f a) ->
exists a, In a l /\ f a.
Proof. intros E [a [[contra | H] fa_true]]; [congruence | now exists a]. Qed.
Fixpoint myFind (l : list A) (H : exists a : A, In a l /\ f a) : {r : A | f r} :=
match l with
| [] => fun H : exists a : A, In a [] /\ f a =>
False_rect {r : A | f r}
match H with
| ex_intro _ _ (conj contra _) =>
match contra with end
end
| h :: l => fun H : exists a : A, In a (h :: l) /\ f a =>
(if f h as b return (f h = b -> {r : A | f r})
then fun Efh => exist _ h Efh
else fun Efh => myFind l (find_not_head Efh H)) eq_refl
end H.
End Find.
这是一个简单的测试:
From Coq Require Import Arith.
Section FindTest.
Notation l := [1; 2; 0; 9].
Notation f := (fun n => n =? 0).
Fact H : exists a, In a l /\ f a.
Proof. exists 0; intuition. Qed.
Compute myFind f l H.
(*
= exist (fun r : nat => f r) 0 eq_refl
: {r : nat | f r}
*)
End FindTest.
答案 1 :(得分:1)
您还可以使用Program
来帮助您交互式地构造证明参数。您将尽可能多地填写程序主体,并留出_
的空白,以备以后使用证明策略填充。
Require Import List Program.
Section Find.
Variable A : Type.
Variable test : A -> bool.
Program Fixpoint FIND l (H:exists a, test a = true /\ In a l) : {r | test r = true} :=
match l with
| [] => match (_:False) with end
| a::l' => if dec (test a) then a else FIND l' _
end.
Next Obligation.
firstorder; congruence.
Defined.
End Find.
Program
在进行案例分析时会更好地避免忘记信息(它知道车队模式),但这并不完美,因此在dec
中使用if
声明。
(请注意,Coq如何独自处理第一项义务,以构造False
类型的术语!)