Coq 如果Simple没有减少所有必要的步骤,应该怎么做?

Coq 如果Simple没有减少所有必要的步骤,应该怎么做?,coq,Coq,以下示例来自《软件基础》一书的第章 Definition fold_length {X : Type} (l : list X) : nat := fold (fun _ n => S n) l 0. Theorem fold_length_correct : forall X (l : list X), fold_length l = length l. Proof. intros. induction l. - simpl. reflexivity. - simpl. 我希

以下示例来自《软件基础》一书的第章

Definition fold_length {X : Type} (l : list X) : nat :=
  fold (fun _ n => S n) l 0.

Theorem fold_length_correct : forall X (l : list X),
  fold_length l = length l.
Proof.
intros.
induction l.
- simpl. reflexivity.
- simpl.
我希望它能简化左侧的步骤。它当然应该能够

Theorem fold_length_correct : forall X (l : list X),
  fold_length l = length l.
Proof.
intros.
induction l.
- simpl. reflexivity.
- simpl. rewrite <- IHl. simpl.
在测试运行期间,我遇到了一个问题,
siml
会拒绝深入,但
reflectivity
做到了这一点,所以我在这里尝试了同样的方法,证明成功了

请注意,鉴于目标的状态,人们不会期望自反性通过,但它确实通过了。在这个例子中,它起了作用,但它确实迫使我以与我最初预期相反的方向进行重写


是否有可能对
siml
进行更多的控制,从而实现所需的缩减?

为了回答这个问题,我假设
折叠的定义与

Fixpoint fold {A B: Type} (f: A -> B -> B) (u: list A) (b: B): B :=
match u with
| [] => b
| x :: v => f x (fold f v b)
end.
(基本上是从标准库中向右折叠)。如果你的定义有很大不同,我推荐的策略可能不起作用


这里的问题是带有常数的
siml
的行为,这些常数必须先展开才能简化。发件人:

请注意,只有名称可以在递归调用中重用的透明常量才可能由siml展开。例如,一个由plus':=plus定义的常量可能会在递归调用中展开和重用,但是像succ:=plus(so)这样的常量永远不会展开

这有点难理解,所以让我们举个例子

Definition add_5 (n: nat) := n + 5.

Goal forall n: nat, add_5 (S n) = S (add_5 n).
Proof.
  intro n.
  simpl.
  unfold add_5; simpl.
  exact eq_refl.
Qed.
您将看到对
simple
的第一次调用没有做任何事情,即使
add_5(sn)
可以简化为
S(n+5)
。但是,如果我先打开add_5,它会工作得很好。我认为问题在于
plus_5
不是直接的
固定点。虽然
plus_5(sn)
等同于
S(plus_5 n)
,但这实际上不是它的定义。因此Coq不承认它的“名称可以在递归调用中重用”
Nat.add
(即“+”)直接定义为递归的
固定点,因此
siml
确实简化了它

可以稍微更改
siml
的行为(请再次参阅文档)。正如Anton在评论中提到的,当
siml
试图简化时,可以使用
Arguments
本地命令进行更改
Arguments fold_length.\uu/
告诉Coq,如果至少提供了两个参数,则应展开
fold_length
(斜线分隔左侧的必需参数和右侧的不必要参数)。[sup]1[\sup]

如果您不想处理这一问题,可以使用一种更简单的策略,即
cbn
,默认情况下,它在这里工作,通常效果更好。引述自:

cbn策略被认为是一种更有原则、更快、更可预测的simpl替代方案

无论是带有
参数的
siml
和斜杠,还是
cbn
都不会将目标降低到您想要的程度,因为它将展开
折叠长度,但不会重新折叠。您可以识别对
fold
的调用只是
fold\u length l
并使用
fold(fold\u length l)
重新折叠它

在您的情况下,另一种可能是使用
更改
策略。似乎您已经知道
fold\u length(a::l)
应该简化为
S(fold\u length l)
。如果是这种情况,您可以使用
change(fold_length(a::l))和(s(fold_length l))。
和Coq将尝试将一个转换为另一个(仅使用基本转换规则,而不是像
rewrite
那样的等式)

在您使用上述任一策略达到
S(fold_length l)=S(length l)
的目标后,您可以使用
rewrite->IHl.


  • 我认为斜杠只会使
    siml
    展开的内容更少,这就是为什么我以前没有提到它的原因。我不确定默认值是什么,因为将斜杠放在任意位置似乎会使
    siml
    展开
    fold\u length

  • 折叠的定义是什么(或者您使用的是什么库)?很抱歉没有指定它。
    fold
    是在我提到的Poly章节中定义的,与您的答案(在重命名下)中的相同。
    siml
    不是一个非常可预测的策略。像这样的案件是作为问题提交的。在这种情况下,指令
    Arguments fold\u length.\uu/
    会有所帮助。Arguments指令中的斜杠表示“在将常量应用于足够多的参数时展开”,其中“足够”是指斜杠前有多少下划线/参数。它可以减少部分应用常数的展开,尽管我只见过它用于诱导更多的展开。
    Fixpoint fold {A B: Type} (f: A -> B -> B) (u: list A) (b: B): B :=
    match u with
    | [] => b
    | x :: v => f x (fold f v b)
    end.
    
    Definition add_5 (n: nat) := n + 5.
    
    Goal forall n: nat, add_5 (S n) = S (add_5 n).
    Proof.
      intro n.
      simpl.
      unfold add_5; simpl.
      exact eq_refl.
    Qed.