我有一个简单的引理清单,上面写着n::l = [n]++l
,其证据显示在下面。
Lemma cons_to_app : (forall (n: nat) (l: list nat), n :: l = [n] ++ l).
Proof.
simpl. reflexivity.
Qed.
现在,我想在证明目标中出现的任何地方都使用此证明重写约束条件::
。例如,考虑以下。
Lemma easy_lemma : forall (n : nat) (xs ys : list nat), (n::xs) ++ (n::ys) = (n::xs) ++ ([n] ++ ys).
我想将(n::ys)
改写为[n] ++ ys
并完成证明。由于n::ys
是::
第二次出现在证明目标中,因此我认为rewrite const_to_app at 2
可以工作,但实际上它作用于第三个::
并改变了证明目标到(n :: xs) ++ n :: ys = ([n] ++ xs) ++ [n] ++ ys
。
我可以指定哪个位置以使(n::ys)
期限的重写工作?
答案 0 :(得分:2)
我仍然找不到谈论rewrite at
(除the one eponier mentioned之外)确切行为的原始资料。链接上的帖子写于2011年,但对于Coq版本8.9.1而言,它似乎在2019年仍然有效,并且可能不会被“修复”,因为该问题已被关闭为“无效”,并说“改变行为会中断”。向后兼容”。
rewrite lemma at n
使用第一个匹配项实例化等式,然后重写其第n个匹配项。
给出引理证明
Lemma easy_lemma :
forall (n : nat) (xs ys : list nat), (n::xs) ++ (n::ys) = (n::xs) ++ ([n] ++ ys).
以及用于重写的引理
Lemma cons_to_app : (forall (n: nat) (l: list nat), n :: l = [n] ++ l).
rewrite cons_to_app at n.
总是选择子项n :: xs
,然后将第n :: xs
的第n个实例重写为[n] ++ xs
。第二个n :: xs
恰好是第三个_ :: _
。
简单的解决方案是提供足够的参数来告诉Coq要重写的确切内容。在这种情况下,rewrite (cons_to_app _ ys)
就足够了。
一种替代方法是使用setoid_rewrite
策略,该策略查看所有适用的子术语。但是,有时候对定义的理解太深了,这个例子的确如此。 setoid_rewrite cons_to_app at 1.
给予
1 subgoal
n : nat
xs, ys : list nat
______________________________________(1/1)
[n] ++
(fix app (l m : list nat) {struct l} : list nat := match l with
| [] => m
| a :: l1 => a :: app l1 m
end) xs (n :: ys) = (n :: xs) ++ [n] ++ ys
折叠app
会得到[n] ++ (xs ++ n :: ys)
,这与我们想要的([n] ++ xs) ++ n :: ys
不同。我们可以观察到setoid_rewrite
展开了app
一次,将LHS更改为n :: (xs ++ n :: ys)
,然后实例化引理来重写最外面的_ :: _
。
为避免展开app
,我们可以在重写之前声明Opaque app.
。然后setoid_rewrite ... at 1
给出了我们想要的(at 2
也给出了)。要恢复Opaque
的效果,请使用Transparent
。