Логика: лексма auxilliry для tr_rev_correct
В главе "Логика" представлена хвостовая рекурсивная версия функции обратного списка. Нам нужно доказать, что это работает правильно:
Fixpoint rev_append {X} (l1 l2 : list X) : list X :=
match l1 with
| [] => l2
| x :: l1' => rev_append l1' (x :: l2)
end.
(* Tail recursion rev *)
Definition tr_rev {X} (l : list X) : list X :=
rev_append l [].
Но прежде чем доказать это, я хотел доказать лемму:
Lemma rev_append_app: forall (X: Type) (x: X) (l : list X),
rev_append l [x] = rev_append l [] ++ [x].
Proof.
intros X x l. induction l as [| h t IH].
- simpl. reflexivity.
- simpl.
Здесь я застрял:
X : Type
x, h : X
t : list X
IH : rev_append t [x] = rev_append t [ ] ++ [x]
============================
rev_append t [h; x] = rev_append t [h] ++ [x]
Что делать дальше?
1 ответ
Как вы заметили во время вашей попытки доказательства, при принятии шага индукции от rev_append l [x]
в rev_append (h :: t) [x]
, вы в конечном итоге с термином rev_append t [h; x]
после упрощения. Шаг индукции не ведет к базовому случаю rev_append
функция, но для другого рекурсивного вызова, который вы не можете упростить.
Обратите внимание, что индукционная гипотеза, которую вы хотите применить, делает заявление о rev_append t [x]
для некоторых исправлено x
, но в вашей цели, экстра h
Элемент списка, прежде чем он встанет на пути, и гипотеза индукции бесполезна.
Это то, на что ссылался Бубблер, когда утверждал, что ваша индуктивная гипотеза недостаточно сильна: он лишь формулирует утверждение в случае, когда вторым аргументом является список с одним элементом. Но даже после всего лишь шага индукции (одно рекурсивное приложение) этот список уже содержит как минимум два элемента!
Как предполагает Bubbler, вспомогательная лемма rev_append l (l1 ++ l2) = rev_append l l1 ++ l2
сильнее и не имеет этой проблемы: при использовании в качестве индукционной гипотезы она может быть применена к rev_append t [h; x]
а также, позволяя вам доказать равенство с rev_append t [h] ++ [x]
,
При попытке доказать лемму помощника, вы можете застрять (как я сделал) так же, как при доказательстве rev_append_app
сам. Важный совет, который помог мне продолжить, заключался в том, чтобы быть осторожным, какую из универсально количественных переменных вы вводите перед началом индукции. Если вы начнете специализировать какой-либо из них слишком рано, вы можете ослабить свою гипотезу об индукции и снова застрять. Возможно, вам придется изменить порядок этих количественных переменных или использовать generalize dependent
тактика (см. главу " Тактика" в Основах логики).
Вы можете видеть, что гипотеза индукции IH
недостаточно силен, чтобы доказать цель. Здесь вам нужно более общее утверждение, чтобы доказать в первую очередь. Вы можете найти больше упражнений, посвященных этой теме здесь. (На самом деле, хвостово-рекурсивный реверс является одним из упражнений.)
В вашем случае полностью обобщенное утверждение может быть следующим:
Lemma rev_append_app': forall (X: Type) (l l1 l2 : list X),
rev_append l (l1 ++ l2) = rev_append l l1 ++ l2.
Доказательство этого по индукции тривиально. Тогда вы можете доказать свое собственное утверждение как следствие этого:
Corollary rev_append_app: forall (X: Type) (x: X) (l : list X),
rev_append l [x] = rev_append l [] ++ [x].
Proof. intros. apply (rev_append_app _ _ [] [x]). Qed.
Используйте обобщенную зависимую тактику следующим образом:
Lemma rev_append_app: forall (X: Type) (l l1: list X) (x : X),
rev_append l (l1 ++ [x]) = rev_append l l1 ++ [x].
intros.
generalize dependent l1.
induction l as [| h t IH].
- intros.
easy.
- intros.
apply (IH (h::l1)).
Qed.