从归纳谓词到列表A - >列表A - >布尔

时间:2016-05-23 19:36:31

标签: coq

在尝试在列表上的归纳谓词上编写可重用代码时,我自然宣称:

Parameter A:Type.

然后我继续定义二元谓词(例如):

Inductive prefix : list A -> list A -> Prop :=
  | prefixNil: forall (l: list A), prefix nil l
  | prefixCons: forall (a: A)(l m:list A), prefix l m -> prefix (a::l) (a::m).

表示给定列表是另一个列表的前缀这一事实。然后,人们可以继续研究这种关系并显示(例如)它是一个偏序。到现在为止还挺好。但是,很容易定义一个与数学概念不符的归纳谓词。我想通过进一步定义函数来验证归纳定义:

isPrefixOf: list A -> list A -> bool

旨在证明等效性:

Theorem prefix_validate: forall (l m: list A), 
  prefix l m <-> isPrefixOf l m = true.

这是我需要限制代码的一般性的地方,因为我无法再使用list A。在Haskell中,我们有isPrefixOf :: Eq a => [a] -> [a] -> Bool,所以我理解我需要在A上做出一些假设,例如&#39; Eq A&#39;。当然,我可以假设:

Parameter equal: A -> A -> bool
Axiom equal_validate: forall (a b: A),
  a = b <-> equal a b = true.

然后从那里开始。我可能会在另一个文件或一个部分中执行此操作,以免破坏前一代码的完整通用性。但是,我觉得我正在重新发明轮子。这可能是一种常见的模式(表达类似Haskell Eq a => ...)。

我应该对类型A做出哪些(惯用的,常见的)声明,允许我继续进行,同时保持代码尽可能通用?

3 个答案:

答案 0 :(得分:4)

(编辑:Coq标准库提供了具有==类型类的Haskell EqDec运算符的直接副本),有关详细信息,请参阅@ anton-trunov答案。)

一般来说,您至少有两个选择:

  • 假设类型A具有可判定的相等性。这可以通过多种形式完成,通常是你想要的

    Hypothesis (A_dec : forall x y : A, {x = y} + {x <> y})
    

    然后,您可以销毁A_dec来比较元素。这用于标准库的几个部分,您可以使用类型类进行自动解析。我相信有几个第三方库使用这种方法(coq-ext-lib,TLC)。代码将成为:

    Require Import Coq.Lists.List.
    Section PrefixDec.
    
    Variable A : Type.
    Hypothesis (A_dec : forall x y : A, {x = y} + {x <> y}).
    Implicit Types (s t : list A).
    
    Fixpoint prefix s t := 
      match s, t with
      | nil, t         => true
      | s, nil         => false
      | x :: s, y :: t => match A_dec x y with
                          | left  _ => prefix s t
                          | right _ => false
                          end
      end.
    
    Inductive prefix_spec : list A -> list A -> Prop :=
      | prefix_nil  : forall (l: list A),
          prefix_spec nil l
      | prefix_cons : forall (a: A) (l m:list A),
          prefix_spec l m -> prefix_spec (a::l) (a::m).
    
    Hint Constructors prefix_spec.
    
    Lemma prefixP s t : prefix_spec s t <-> prefix s t = true.
    Proof.
    revert t; induction s as [|x s]; intros [|y t]; split; simpl; try congruence; auto.
    + now intros H; inversion H.
    + destruct (A_dec x y); simpl; intros H; inversion H; subst; try congruence.
      now rewrite <- IHs.
    + destruct (A_dec x y); intros H; inversion H; subst.
      now constructor; rewrite IHs.
    Qed.
    
    End PrefixDec.
    
    (* Compute example *)
    Import ListNotations.
    Compute (prefix _ PeanoNat.Nat.eq_dec [2; 3; 4] [ 2; 3; 4; 5]).
    Compute (prefix _ PeanoNat.Nat.eq_dec [2; 3; 4] [ 2; 3; 5]).
    
  • 按照您的方法提供一个布尔等于运算符。 math-comp库提供了一个类层次结构,其中包含一类具有可判定等式eqType的类型。因此,对于A : eqType, x y : A,您可以使用==运算符来比较它们。有关列表的示例,请参阅http://math-comp.github.io/math-comp/htmldoc/mathcomp.ssreflect.seq.html

    您的equalvalidate假设完全封装在eqType(名为==eqP)中。代码是:

    From mathcomp
    Require Import ssreflect ssrfun ssrbool eqtype ssrnat seq.
    
    Section PrefixEq.
    
    Variable A : eqType.
    Implicit Types (s t : seq A).
    
    Fixpoint prefix s t :=
      match s, t with
      | nil, t          => true
      | s, nil          => false
      | x :: s, y :: t => if x == y then prefix s t else false
      end.
    
    Inductive prefix_spec : list A -> list A -> Prop :=
      | prefix_nil  : forall (l: list A),
          prefix_spec nil l
      | prefix_cons : forall (a: A) (l m:list A),
          prefix_spec l m -> prefix_spec (a::l) (a::m).
    
    Hint Constructors prefix_spec.
    
    Lemma prefixP s t : reflect (prefix_spec s t) (prefix s t).
    Proof.
    apply: (iffP idP); elim: s t => // x s ihs [|y t] //=.
    - by case: eqP => // ->; auto.
    - by move=> H; inversion H.
    - by move=> H; inversion H; subst; rewrite eqxx ihs.
    Qed.
    
    End PrefixEq.
    
    Compute (prefix _ [:: 1;2;3] [:: 1;2;3]).
    Compute (prefix _ [:: 1;2;3] [:: 1;3;3]).
    

    关于math-comp方法的一个好处是它会自动推断eqType类型的nat实例。这有助于保持样张轻量级。关于上述证据的注释是我会通过使用和“反转视图”来避免反转,但这是一个不同的主题。此外,使用现有的seq.subseq可能更有意义,或者您可能需要类似的内容:

    Definition is_prefix (A : eqType) (s t : seq A) := s == take (size s) t.
    

什么更惯用?恕我直言,这取决于。 AFAICT不同的开发人员喜欢不同的技术。我发现第二种方法对我来说效果最好,代价是在证明中有一些额外的eqP个应用程序。

代码在这里:https://x80.org/collacoq/akumoyutaz.coq

答案 1 :(得分:3)

要完成@ ejgallego的回答,您还可以使用模块系统做出相应的假设。如果你Require Import Structures.Equalities,你有一些有用的模块类型。例如,Typ只包含t类型,而UsualBoolEq则假定存在运算符eqb : t -> t -> bool验证eqb_eq : forall x y : t, eqb x y = true <-> x = y

您可以将您的定义放在仿函数中。

Require Import Structures.Equalities.
Require Import List. Import ListNotations.
Require Import Bool.

Module Prefix (Import T:Typ).
  Inductive prefix : list t -> list t -> Prop :=
  | prefixNil: forall (l: list t), prefix nil l
  | prefixCons: forall (a: t)(l m:list t), prefix l m -> prefix (a::l) (a::m).
End Prefix.

Module PrefixDecidable (Import T:UsualBoolEq).
  Include Prefix T.

  Fixpoint isPrefixOf (l m : list t) :=
    match l with
    | [] => true
    | a::l' =>
      match m with
      | [] => false
      | b::m' => andb (eqb a b) (isPrefixOf l' m')
      end
    end.

  Theorem prefix_validate: forall (l m: list t), 
    prefix l m <-> isPrefixOf l m = true.
  Proof.
    split; intros H.
    - induction H.
      + reflexivity.
      + simpl. rewrite andb_true_iff. split; [|assumption].
        apply eqb_eq; reflexivity.
    - revert dependent m; induction l as [|a l']; intros m H.
      + constructor.
      + destruct m as [|b m'].
        * discriminate.
        * simpl in H. rewrite andb_true_iff in H. destruct H as [H H0].
          apply eqb_eq in H. subst b.
          constructor. apply IHl'; assumption.
  Qed.
End PrefixDecidable.

但请注意,模块系统不是最方便使用的Coq的一部分。我倾向于选择@ejgallego提供的选项。这个答案主要是为了完整性。

答案 2 :(得分:2)

又一个版本,使用可判定的等价(Coq.Classes.EquivDec)。

Require Import Coq.Bool.Bool.
Require Import Coq.Lists.List. Import ListNotations.
Require Import Coq.Classes.EquivDec.

Set Implicit Arguments.

Section Prefix.

  Variable A : Type.
  Context `{EqDec A eq}.  (* A has decidable equivalence *)

  Inductive prefix : list A -> list A -> Prop :=
  | prefixNil: forall (l: list A), prefix nil l
  | prefixCons: forall (a: A)(l m:list A), prefix l m -> prefix (a::l) (a::m).
  Hint Constructors prefix.

  Fixpoint isPrefixOf (l1 l2 : list A) : bool :=
    match l1, l2 with
    | [], _ => true
    | _, []   => false
    | h1 :: t1, h2 :: t2 => if h1 == h2 then isPrefixOf t1 t2
                            else false
    end.

  Theorem prefix_validate : forall (l m: list A), 
      prefix l m <-> isPrefixOf l m = true.
  Proof.
    induction l; split; intro Hp; auto; destruct m; inversion Hp; subst.
    - simpl. destruct (equiv_dec a0 a0).
      + apply IHl. assumption.
      + exfalso. apply c. reflexivity.
    - destruct (equiv_dec a a0).
      + rewrite e. constructor. apply IHl. assumption.
      + discriminate H1.
  Qed.
End Prefix.

让我提供一些使用isPrefixOf进行计算的示例。对于nat来说,它非常简单,因为nat已经是EqDec的一个实例:

Eval compute in isPrefixOf [1;2] [1;2;3;4]. (* = true *)
Eval compute in isPrefixOf [1;9] [1;2;3;4]. (* = false *)

这是对用户定义类型的测试:

Inductive test : Type :=
  | A : test
  | B : test
  | C : test.
Lemma test_dec : forall x y:test, {x = y} + {x <> y}.
Proof. decide equality. Defined.

Instance test_eqdec : EqDec test _ := test_dec.

Eval compute in isPrefixOf [A;B] [A;B;C;A]. (* = true *)
Eval compute in isPrefixOf [A;C] [A;B;C;A]. (* = false *)