Coq 在使用重写策略时找不到子项
Coq can't find subterm when using rewrite tactic
我正在尝试从 Certified Programming with Dependent Types 的 first chapter 中修改 compile_correct
的证明。在我的版本中,我尝试利用 progDenote
是一个折叠的事实,并在 priving compile_correct
.
的主要引理的证明中使用较弱的归纳假设
与书中相同的代码为:
Require Import Bool Arith List.
Set Implicit Arguments.
Inductive binop : Set := Plus | Times.
Inductive exp : Set :=
| Const : nat -> exp
| Binop : binop -> exp -> exp -> exp.
Definition binopDenote (b : binop) : nat -> nat -> nat :=
match b with
| Plus => plus
| Times => mult
end.
Fixpoint expDenote (e : exp) : nat :=
match e with
| Const n => n
| Binop b e1 e2 => (binopDenote b) (expDenote e1) (expDenote e2)
end.
Inductive instr : Set :=
| iConst : nat -> instr
| iBinop : binop -> instr.
Definition prog := list instr.
Definition stack := list nat.
Definition instrDenote (i : instr) (s : stack) : option stack :=
match i with
| iConst n => Some (n :: s)
| iBinop b =>
match s with
| arg1 :: arg2 :: s' => Some ((binopDenote b) arg1 arg2 :: s')
| _ => None
end
end.
Fixpoint compile (e : exp) : prog :=
match e with
| Const n => iConst n :: nil
| Binop b e1 e2 => compile e2 ++ compile e1 ++ iBinop b :: nil
end.
然后我定义了我自己的 prog_denote
版本,它是程序指令列表的折叠:
Definition bind {A B : Type} (a : option A) (f : A -> option B) : option B :=
match a with
| Some x => f x
| None => None
end.
Definition instrDenote' (s : option stack) (i : instr) : option stack :=
bind s (instrDenote i).
Definition progDenote (p : prog) (s : stack) : option stack :=
fold_left instrDenote' p (Some s).
然后我尝试从书中证明 compile_correct
的较弱版本:
Lemma compile_correct' : forall e s,
progDenote (compile e) s = Some (expDenote e :: s).
induction e.
intro s.
unfold compile.
unfold expDenote.
unfold progDenote at 1.
simpl.
reflexivity.
intro s.
unfold compile.
fold compile.
unfold expDenote.
fold expDenote.
unfold progDenote.
rewrite fold_left_app.
rewrite fold_left_app.
unfold progDenote in IHe2.
rewrite (IHe2 s).
unfold progDenote in IHe1.
rewrite (IHe1 (expDenote e2 :: s)).
我的证明在最后一行中断,带有证明状态
1 subgoal
b : binop
e1 : exp
e2 : exp
IHe1 : forall s : stack,
fold_left instrDenote' (compile e1) (Some s) =
Some (expDenote e1 :: s)
IHe2 : forall s : stack,
fold_left instrDenote' (compile e2) (Some s) =
Some (expDenote e2 :: s)
s : stack
______________________________________(1/1)
fold_left instrDenote' (iBinop b :: nil)
(fold_left instrDenote' (compile e1) (Some (expDenote e2 :: s))) =
Some (binopDenote b (expDenote e1) (expDenote e2) :: s)
错误是
Error:
Found no subterm matching "fold_left instrDenote' (compile e1)
(Some (expDenote e2 :: s))" in the current goal.
在证明的这个阶段,我正在对 e
、正在编译的表达式进行归纳,并处理 exp
的 Binop
构造函数。我不明白为什么会出现此错误,因为一旦我将 IHe1
应用于 expDenote e2 :: s
就没有绑定变量。这似乎是应用重写规则不起作用的常见问题。我还检查了我要创建的术语:
fold_left instrDenote' (iBinop b :: nil)
(Some (expDenote e1 :: expDenote e2 :: s)) =
Some (binopDenote b (expDenote e1) (expDenote e2) :: s)
类型检查。
当重写规则所抱怨的子表达式显然存在于目标中时,重写规则还会出什么问题?
编辑:按照建议,我将 coqide 中的显示设置更改为相当于设置打印全部。这揭示了问题是 stack
的定义在目标中的一个地方被展开为 list nat
,这阻止了子项被识别。使用新设置打印的目标是
1 subgoal
b : binop
e1 : exp
e2 : exp
IHe1 : forall s : stack,
@eq (option stack)
(@fold_left (option stack) instr instrDenote' (compile e1)
(@Some stack s)) (@Some (list nat) (@cons nat (expDenote e1) s))
IHe2 : forall s : stack,
@eq (option stack)
(@fold_left (option stack) instr instrDenote' (compile e2)
(@Some stack s)) (@Some (list nat) (@cons nat (expDenote e2) s))
s : stack
______________________________________(1/1)
@eq (option stack)
(@fold_left (option stack) instr instrDenote'
(@cons instr (iBinop b) (@nil instr))
(@fold_left (option stack) instr instrDenote' (compile e1)
(@Some (list nat) (@cons nat (expDenote e2) s))))
(@Some (list nat)
(@cons nat (binopDenote b (expDenote e1) (expDenote e2)) s))
错误是
Error:
Found no subterm matching "@fold_left (option stack) instr instrDenote'
(compile e1)
(@Some stack (@cons nat (expDenote e2) s))" in the current goal.
即使使用默认显示设置,子项似乎出现在目标中,启用 Set Printing All
,很明显子项与目标不匹配,因为在目标中,stack
已展开至list nat
。所以需要 fold stack
将 list nat
变回目标中的 stack
。
作为初学者,我似乎被以下组合绊倒了:
unfold
策略展开的定义比初学者预期的要多。
默认显示设置(在我的例子中是在 CoqIDE 中)可以隐藏它,因为它们折叠了一些术语。
感谢 Arthur Azevedo De Amorim 建议启用 Set Printing All
。
我正在尝试从 Certified Programming with Dependent Types 的 first chapter 中修改 compile_correct
的证明。在我的版本中,我尝试利用 progDenote
是一个折叠的事实,并在 priving compile_correct
.
与书中相同的代码为:
Require Import Bool Arith List.
Set Implicit Arguments.
Inductive binop : Set := Plus | Times.
Inductive exp : Set :=
| Const : nat -> exp
| Binop : binop -> exp -> exp -> exp.
Definition binopDenote (b : binop) : nat -> nat -> nat :=
match b with
| Plus => plus
| Times => mult
end.
Fixpoint expDenote (e : exp) : nat :=
match e with
| Const n => n
| Binop b e1 e2 => (binopDenote b) (expDenote e1) (expDenote e2)
end.
Inductive instr : Set :=
| iConst : nat -> instr
| iBinop : binop -> instr.
Definition prog := list instr.
Definition stack := list nat.
Definition instrDenote (i : instr) (s : stack) : option stack :=
match i with
| iConst n => Some (n :: s)
| iBinop b =>
match s with
| arg1 :: arg2 :: s' => Some ((binopDenote b) arg1 arg2 :: s')
| _ => None
end
end.
Fixpoint compile (e : exp) : prog :=
match e with
| Const n => iConst n :: nil
| Binop b e1 e2 => compile e2 ++ compile e1 ++ iBinop b :: nil
end.
然后我定义了我自己的 prog_denote
版本,它是程序指令列表的折叠:
Definition bind {A B : Type} (a : option A) (f : A -> option B) : option B :=
match a with
| Some x => f x
| None => None
end.
Definition instrDenote' (s : option stack) (i : instr) : option stack :=
bind s (instrDenote i).
Definition progDenote (p : prog) (s : stack) : option stack :=
fold_left instrDenote' p (Some s).
然后我尝试从书中证明 compile_correct
的较弱版本:
Lemma compile_correct' : forall e s,
progDenote (compile e) s = Some (expDenote e :: s).
induction e.
intro s.
unfold compile.
unfold expDenote.
unfold progDenote at 1.
simpl.
reflexivity.
intro s.
unfold compile.
fold compile.
unfold expDenote.
fold expDenote.
unfold progDenote.
rewrite fold_left_app.
rewrite fold_left_app.
unfold progDenote in IHe2.
rewrite (IHe2 s).
unfold progDenote in IHe1.
rewrite (IHe1 (expDenote e2 :: s)).
我的证明在最后一行中断,带有证明状态
1 subgoal
b : binop
e1 : exp
e2 : exp
IHe1 : forall s : stack,
fold_left instrDenote' (compile e1) (Some s) =
Some (expDenote e1 :: s)
IHe2 : forall s : stack,
fold_left instrDenote' (compile e2) (Some s) =
Some (expDenote e2 :: s)
s : stack
______________________________________(1/1)
fold_left instrDenote' (iBinop b :: nil)
(fold_left instrDenote' (compile e1) (Some (expDenote e2 :: s))) =
Some (binopDenote b (expDenote e1) (expDenote e2) :: s)
错误是
Error:
Found no subterm matching "fold_left instrDenote' (compile e1)
(Some (expDenote e2 :: s))" in the current goal.
在证明的这个阶段,我正在对 e
、正在编译的表达式进行归纳,并处理 exp
的 Binop
构造函数。我不明白为什么会出现此错误,因为一旦我将 IHe1
应用于 expDenote e2 :: s
就没有绑定变量。这似乎是应用重写规则不起作用的常见问题。我还检查了我要创建的术语:
fold_left instrDenote' (iBinop b :: nil)
(Some (expDenote e1 :: expDenote e2 :: s)) =
Some (binopDenote b (expDenote e1) (expDenote e2) :: s)
类型检查。
当重写规则所抱怨的子表达式显然存在于目标中时,重写规则还会出什么问题?
编辑:按照建议,我将 coqide 中的显示设置更改为相当于设置打印全部。这揭示了问题是 stack
的定义在目标中的一个地方被展开为 list nat
,这阻止了子项被识别。使用新设置打印的目标是
1 subgoal
b : binop
e1 : exp
e2 : exp
IHe1 : forall s : stack,
@eq (option stack)
(@fold_left (option stack) instr instrDenote' (compile e1)
(@Some stack s)) (@Some (list nat) (@cons nat (expDenote e1) s))
IHe2 : forall s : stack,
@eq (option stack)
(@fold_left (option stack) instr instrDenote' (compile e2)
(@Some stack s)) (@Some (list nat) (@cons nat (expDenote e2) s))
s : stack
______________________________________(1/1)
@eq (option stack)
(@fold_left (option stack) instr instrDenote'
(@cons instr (iBinop b) (@nil instr))
(@fold_left (option stack) instr instrDenote' (compile e1)
(@Some (list nat) (@cons nat (expDenote e2) s))))
(@Some (list nat)
(@cons nat (binopDenote b (expDenote e1) (expDenote e2)) s))
错误是
Error:
Found no subterm matching "@fold_left (option stack) instr instrDenote'
(compile e1)
(@Some stack (@cons nat (expDenote e2) s))" in the current goal.
即使使用默认显示设置,子项似乎出现在目标中,启用 Set Printing All
,很明显子项与目标不匹配,因为在目标中,stack
已展开至list nat
。所以需要 fold stack
将 list nat
变回目标中的 stack
。
作为初学者,我似乎被以下组合绊倒了:
unfold
策略展开的定义比初学者预期的要多。默认显示设置(在我的例子中是在 CoqIDE 中)可以隐藏它,因为它们折叠了一些术语。
感谢 Arthur Azevedo De Amorim 建议启用 Set Printing All
。