为什么构造函数在这里花了这么长时间?

时间:2017-09-14 19:56:32

标签: coq

请考虑以下代码:

Inductive Even : nat -> Prop :=
| EO : Even O
| ESS : forall n, Even n -> Even (S (S n)).

Fixpoint is_even_prop (n : nat) : Prop :=
  match n with
  | O => True
  | S O => False
  | S (S n) => is_even_prop n
  end.

Theorem is_even_prop_correct : forall n, is_even_prop n -> Even n.
Admitted.

Example Even_5000 : Even 5000.
Proof.
  apply is_even_prop_correct.

  Time constructor. (* ~0.45 secs *)
  Undo.

  Time (constructor 1). (* ~0.25 secs *)
  Undo.

  (* The documentation for constructor says that "constructor 1"
     should be the same thing as doing this: *)
  Time (apply I). (* ~0 secs *)
  Undo.

  (* Apparently, if there's only one applicable constructor,
     reflexivity falls back on constructor and consequently
     takes as much time as that tactic: *)
  Time reflexivity. (* Around ~0.45 secs also *)
  Undo.

  (* If we manually reduce before calling constructor things are
     faster, if we use the right reduction strategy: *)
  Time (cbv; constructor). (* ~0 secs *)
  Undo.

  Time (cbn; constructor). (* ~0.5 secs *)
Qed.

Theorem is_even_prop_correct_fast : forall n, is_even_prop n = True -> Even n.
Admitted.

Example Even_5000_fast : Even 5000.
Proof.
  apply is_even_prop_correct_fast.

  (* Everything here is essentially 0 secs: *)
  Time constructor.
  Undo.
  Time reflexivity.
  Undo.
  Time (apply eq_refl). Qed.

我只是想知道你是否可以在Prop而不是Set进行反思,并偶然发现了这一点。我的问题不是如何正确地进行反射,我只是想知道为什么constructor在第一种情况下与第二种情况相比如此缓慢。 (也许它与constructor有关,可以立即看到(没有任何约简)构造函数在第二种情况下必须eq_refl?但它必须在之后减少......)

此外,在试图找出constructor正在做什么时,我注意到文档没有说明策略将采用哪种减少策略。这个遗漏是故意的吗?我的想法是你应该明确说明你想要哪种减少策略(如果你特别想要一个减少策略)(否则实现可以自由选择)?

1 个答案:

答案 0 :(得分:4)

简短的回答:它花时间试图找出你的目标所属的归纳家庭(在constructor的情况下是两倍),使用hnf

更长的答案:做一些源头潜水,看起来像constructor calls Tactics.any_constructor,而constructor 1 calls Tactics.constructor_tacTactics.any_constructor依次调用Tacmach.New.pf_apply Tacred.reduce_to_quantified_ind来确定计算构造函数的归纳类型,然后依次对每个可能的构造函数调用Tactics.constructor_tac。对于True,因为有一个构造函数,它暗示constructor的时间大约是constructor 1的两倍;我猜测时间花在reduce_to_quantified_ind上。反过来,Tacred.reduce_to_quantified_ind会调用reduce_to_ind_gen,然后调用hnf_constr。事实上,看起来Time hnfTime constructor 1大致相同。此外,Time constructor是在手动hnf之后的瞬间。我不确定hnf内部使用的策略。文档遗漏几乎肯定不是故意的(至少,无论目前的策略应该出现在脚注中,我认为,所以随时报告错误),但我不清楚使用的减少策略constructor确定您的目标所属的归纳家庭应该成为constructor规范的一部分。