Coq 列表命题的归纳原则(或:嵌套列表表达式的LNR)

Coq 列表命题的归纳原则(或:嵌套列表表达式的LNR),coq,induction,Coq,Induction,免责声明:我担心这篇文章太长了,但是,我觉得在一个较小的设置中,一些有价值的背景信息会丢失 我目前正试图改变我的形式化,使用Charguéraud等人[1]的本地无名表示法。显然,这种调整并不像我希望的那样简单,因为我对表达式的定义包含列表(至少我目前认为这是主要问题) 因此,我对表达式有以下(最小)定义 Require Import Coq.Lists.List. Require Import Coq.Arith.PeanoNat. Parameter atom : Set. Paramet

免责声明:我担心这篇文章太长了,但是,我觉得在一个较小的设置中,一些有价值的背景信息会丢失

我目前正试图改变我的形式化,使用Charguéraud等人[1]的本地无名表示法。显然,这种调整并不像我希望的那样简单,因为我对表达式的定义包含列表(至少我目前认为这是主要问题)

因此,我对表达式有以下(最小)定义

Require Import Coq.Lists.List.
Require Import Coq.Arith.PeanoNat.

Parameter atom : Set.
Parameter eq_atom_dec : forall x y : atom, {x = y} + {x <> y}.

Definition VarIndex := nat.

Inductive Expr : Type :=
 | BVar : VarIndex -> VarIndex -> Expr
 | FVar : atom -> Expr
 | LetB : list Expr -> Expr -> Expr.
到目前为止还不错。接下来,归纳地定义了“局部闭合”的性质,如下所示

Inductive lc : Expr -> Prop :=
| lc_var : forall x,
    lc (FVar x)
| lc_let : forall (ts: list Expr) es e,
    Forall lc es ->
    lc (open e ts) ->
    lc (LetB es e).
本教程现在指出,我们可以证明一个关于
lc
open
相互作用的引理,即在局部封闭表达式中,替换变量时不会发生任何情况

(* this is a auxiliary lemma that works just fine for me *)
Lemma open_rec_lc_core : forall e (j: VarIndex) v (i: VarIndex) u,
    i <> j ->
    {j ~> v} e = {i ~> u} ({j ~> v} e) ->
    e = {i ~> u} e.
Proof.
Admitted.

Lemma open_rec_lc0 : forall k u e,
    lc e ->
    e = {k ~> u} e.
Proof.
  intros k u e LC.
  generalize dependent k.
  induction LC; intro k.
  - reflexivity.
  - simpl.
    f_equal.
    + admit.
    + eapply open_rec_lc_core with (j := 0).
      * auto.
      * eapply IHLC.         
Admitted.
我需要的是一个与IHLC相同的假设,但是对于
es
。 我的第一个猜测是,我需要修改归纳原则,因为它通常用于列表作为参数的归纳定义[2]。 但是,我无法制定一个实际进行类型检查的定义

Fail Definition lc_ind2 :=
  fun (P : Expr -> Prop) (f : forall x : atom, P (FVar x))
    (f0 : forall (ts es : list Expr) (e : Expr),
        Forall lc (map (fun e' : Expr => open e' ts) es) ->
        lc (open e ts) -> P (open e ts) ->
        Forall P (map (fun e' => open e' ts ) es) ->
        P (LetB es e)) =>
    fix F (e : Expr) (l : lc e) {struct l} : P e :=
    match l in (lc e0) return (P e0) with
    | lc_var x => f x
    | lc_let ts es e0 f1 l0 =>
      f0 ts es e0 f1 l0 (F (open e0 ts) l0)
         ((fix F' (es: list Expr) : Forall P es :=
                     match es with
                     | nil => Forall_nil P
                     | cons x xs => Forall_cons x (F x _) (F' xs)
                     end) (map (fun e' => open e' ts) es))
    end.
我需要的是
lcx
类型的东西,而不是
在所有的
应用程序中,但是我不知道如何得出这个值

所以,最后我的问题是,如果有人知道为了使用LNR,我需要修改哪些定义

[1]

[2]

好的,最后我只是将
for all
内联到一个使用
lc
的局部归纳定义中

Inductive lc : Expr -> Prop :=
| lc_var : forall x,
    lc (FVar x)
| lc_let : forall (ts: list Expr) es e,
    Forall_lc es ->
    lc (open e ts) ->
    lc (LetB es e).
with Forall_lc : list Expr -> Prop :=
     | nil_lc : Forall_lc nil
     | cons_lc : forall e es, lc e -> Forall_lc es -> Forall_lc (e :: es).
生成了我需要的归纳原理

Scheme lc2_ind := Minimality for lc Sort Prop
  with lc_Forall_ind := Minimality for Forall_lc Sort Prop.
采取了同样的办法。
我想,最后的诀窍是使用相互递归的定义,而不是尝试将
lc
作为
Forall
的参数,好的,所以最后我只是将
Forall
内联到一个使用
lc
的局部归纳定义中

Inductive lc : Expr -> Prop :=
| lc_var : forall x,
    lc (FVar x)
| lc_let : forall (ts: list Expr) es e,
    Forall_lc es ->
    lc (open e ts) ->
    lc (LetB es e).
with Forall_lc : list Expr -> Prop :=
     | nil_lc : Forall_lc nil
     | cons_lc : forall e es, lc e -> Forall_lc es -> Forall_lc (e :: es).
生成了我需要的归纳原理

Scheme lc2_ind := Minimality for lc Sort Prop
  with lc_Forall_ind := Minimality for Forall_lc Sort Prop.
采取了同样的办法。
我想,最后的诀窍是使用相互递归的定义,而不是尝试将
lc
作为所有
Forall

的参数。我不明白所有的细节。例如,在第一个证明中,必须对所有
假设直接进行归纳,而不是对es进行归纳,以尊重保护条件。还要注意使用了
refine
,它允许通过将下划线留给未知参数并逐步完成,以迭代方式构建术语

Lemma lc_ind2 : forall P : Expr -> Prop,
  (forall x : atom, P (FVar x)) ->
  (forall (ts es : list Expr) (e : Expr),
  Forall lc es -> Forall P es ->
  lc (open e ts) -> P (open e ts) -> P (LetB es e)) ->
  forall e : Expr, lc e -> P e.
Proof.
  intros. revert e H1.
  refine (fix aux e H1 (* {struct H1} *) := match H1 with
  | lc_var x => H x
  | lc_let ts es e HFor Hlc => H0 ts es e HFor _ Hlc (aux (open e ts) Hlc)
  end).
  induction HFor.
  constructor.
  constructor.
  apply aux. apply H2. assumption.
Qed.

Lemma Forall_map : forall {A} f (l:list A),
  Forall (fun x => x = f x) l ->
  l = map f l.
Proof.
  intros.
  induction H.
  reflexivity.
  simpl. f_equal; assumption.
Qed.

Lemma open_rec_lc0 : forall k u e,
    lc e ->
    e = {k ~> u} e.
Proof.
  intros k u e H. revert k u.
  induction H using lc_ind2; intros.
  - reflexivity.
  - simpl. f_equal.
    + apply Forall_map. apply Forall_forall. rewrite Forall_forall in H0.
      intros. apply H0. assumption.
    + eapply open_rec_lc_core with (j := 0).
      * auto.
      * eapply IHlc.
Qed.

这里有一个有效的解决方案。我不明白所有的细节。例如,在第一个证明中,必须对所有假设直接进行归纳,而不是对es进行归纳,以尊重保护条件。还要注意使用了
refine
,它允许通过将下划线留给未知参数并逐步完成,以迭代方式构建术语

Lemma lc_ind2 : forall P : Expr -> Prop,
  (forall x : atom, P (FVar x)) ->
  (forall (ts es : list Expr) (e : Expr),
  Forall lc es -> Forall P es ->
  lc (open e ts) -> P (open e ts) -> P (LetB es e)) ->
  forall e : Expr, lc e -> P e.
Proof.
  intros. revert e H1.
  refine (fix aux e H1 (* {struct H1} *) := match H1 with
  | lc_var x => H x
  | lc_let ts es e HFor Hlc => H0 ts es e HFor _ Hlc (aux (open e ts) Hlc)
  end).
  induction HFor.
  constructor.
  constructor.
  apply aux. apply H2. assumption.
Qed.

Lemma Forall_map : forall {A} f (l:list A),
  Forall (fun x => x = f x) l ->
  l = map f l.
Proof.
  intros.
  induction H.
  reflexivity.
  simpl. f_equal; assumption.
Qed.

Lemma open_rec_lc0 : forall k u e,
    lc e ->
    e = {k ~> u} e.
Proof.
  intros k u e H. revert k u.
  induction H using lc_ind2; intros.
  - reflexivity.
  - simpl. f_equal.
    + apply Forall_map. apply Forall_forall. rewrite Forall_forall in H0.
      intros. apply H0. assumption.
    + eapply open_rec_lc_core with (j := 0).
      * auto.
      * eapply IHlc.
Qed.

什么是
VarIndex
?哦,对不起,这只是对
nat
的重命名,但是谢谢你发现了这一点,我编辑了这篇文章。(1)为什么
BVar
有两个索引?他们非正式的意思是什么?(2)
LetB
非正式地代表了什么?(1)是(2)的结果
LetB
表示多个let绑定,即“let x=31;y=x+2”,并且
BVar i j
有两个索引来精确处理这些绑定。
i
表示您必须通过多少个活页夹才能到达您的声明,对于let活页夹,
j
表示要使用这些多活页夹中的哪一个(例如,在上述let表达式中,
1
表示x,
2
表示y)。ichistmeinname在第7.2章中也讨论了该方法。@ichistmeinname我对
open\u rec\u lc\u core
的证明很感兴趣,你是如何证明的?什么是
VarIndex
?哦,对不起,这只是对
nat
的重命名,但感谢你发现了这一点,我编辑了这篇文章。(1)为什么
BVar
有两个索引?他们非正式的意思是什么?(2)
LetB
非正式地代表了什么?(1)是(2)的结果
LetB
表示多个let绑定,即“let x=31;y=x+2”,并且
BVar i j
有两个索引来精确处理这些绑定。
i
表示您必须通过多少个活页夹才能到达您的声明,对于let活页夹,
j
表示要使用这些多活页夹中的哪一个(例如,在上述let表达式中,
1
表示x,
2
表示y)。在第7.2章中,ichistmeinname也讨论了该方法。@ichistmeinname我对
open\u rec\u lc\u core
的证明感兴趣,您是如何证明它的?实际上我正试图将函数逻辑编程语言形式化,但
let
-部分实际上只是一个函数组件!:)我记得我用一个对列表形式化了let,每个节点表示一个ident和一个lamba术语之间的对应关系实际上我正试图形式化一种函数逻辑编程语言,但是
let
-部分实际上只是一个函数组件!:)我记得我用一个对列表形式化了let,每个节点表示一个ident和一个lamba term之间的对应关系。我终于花了时间在我当前的设置中调整了您的方法,它工作得非常好!我以前不知道
refine
是如何工作的,所以感谢您为我介绍了一种新策略。这对将来肯定也有帮助。我终于花时间在我目前的环境中调整了你的方法,效果非常好!我以前不知道
refine
是如何工作的,所以感谢您为我介绍了一种新策略。这在将来肯定也有帮助。