Я доказал «правильность» обратной функции на полиморфных Списках в Coq.Следующее доказательство работает отлично, но у меня есть несколько вопросов о том, как работает тактика rewrite .
Вот код:
Require Export Coq.Lists.List.
Import ListNotations.
Fixpoint rev {T:Type} (l:list T) : list T :=
match l with
| nil => nil
| h :: t => rev t ++ [h]
end.
(* Prove rev_acc equal to above naive implementation. *)
Fixpoint rev_acc {T:Type} (l acc:list T) : list T :=
match l with
| nil => acc
| h :: t => rev_acc t (h::acc)
end.
Theorem app_assoc : forall (T:Type) (l1 l2 l3 : list T),
(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
Admitted.
Theorem rev_acc_correct : forall (T:Type) (l k :list T),
rev l ++ k = rev_acc l k.
Proof.
intros T l.
induction l as [ | h l' IHl' ].
- reflexivity.
- simpl.
intro k.
(* Why is "intro k" required for "rewrite -> app_assoc" *)
(* But "rewrite -> IHl'" works regardless of "intro k". *)
(* generalize (rev l'), [h], k. *)
rewrite -> app_assoc.
simpl.
rewrite -> IHl'.
reflexivity.
Qed.
В индуктивном шагедоказательство для rev_acc_correct , если я пропущу intro k , а затем переписать с помощью app_assoc , жалуется, что не может найти подходящий подтерм.
Found no subterm matching "(?M1058 ++ ?M1059) ++ ?M1060" in the current goal.
Здесь я предполагаю, что ? перед именами заполнителей означают, что термины ограничены, в данном случае они имеют тип Список T для некоторого типа T ;и поскольку rev l ' и [h] в воротах являются экземплярами списка T , можно ожидать совпадения в воротах.
С другой стороны, переписывание с индуктивной гипотезой ( rewrite -> IHl ') вместо app_assoc проходит без необходимости intro k до этого.
Мне кажется, что такое поведение rewrite немного сбивает с толку, и руководство Coq не дает никаких подробностей.Я не хочу разбираться с реализацией, но мне нужно хорошее понимание того, что делает тактика перезаписи, особенно в отношении того, как работает сопоставление терминов.Любые ответы / рекомендации в этом направлении приветствуются.