У меня есть доказательство ниже с еще тремя подцелями. Доказательство касается правильности оптимизации plus 0
(optimize_0plus
) на простом арифметическом языке, продемонстрированном здесь. aexp
- это «арифметическое выражение», а aeval
- «арифметическая оценка».
3 subgoal
a1 : aexp
a2 : aexp
IHa1 : aeval (optimize_0plus a1) = aeval a1
IHa2 : aeval (optimize_0plus a2) = aeval a2
______________________________________(1/3)
aeval (optimize_0plus (APlus a1 a2)) = aeval (APlus a1 a2)
, где optimize_0plus
:
Fixpoint optimize_0plus (a:aexp) : aexp :=
match a with
| ANum n =>
ANum n
| APlus (ANum 0) e2 =>
optimize_0plus e2
| APlus e1 e2 =>
APlus (optimize_0plus e1) (optimize_0plus e2)
| AMinus e1 e2 =>
AMinus (optimize_0plus e1) (optimize_0plus e2)
| AMult e1 e2 =>
AMult (optimize_0plus e1) (optimize_0plus e2)
end.
Мой военный план состоит в том, чтобы применить optimize_0plus
в LHS текущей подцели и получить:
aeval (APlus (optimize_0plus a1) (optimize_0plus a2)) = aeval (APlus a1 a2)
(Но я не могу понять, как это сделать в Coq).
Затем через несколько simpl
получить:
(aeval (optimize_0plus a1)) + (aeval (optimize_0plus a2)) = (aeval a1) + (aeval a2)
и примените предположения индукции IHa1
и IHa2
, чтобы завершить доказательство.
У меня вопрос:
Как я могу указать Coq применить определение optimize_0plus
ровно один раз и не делать больше и не меньше?
Я пробовал simpl optimize_0plus
, но он дает что-то с длинным оператором match
, который, кажется, делает слишком много. И мне не нравится использовать тактику rewrite
каждый раз, чтобы установить лемму, потому что это вычисление - это ровно один шаг с бумагой и карандашом.
Примечания:
1. Это связано с моим более ранним вопросом здесь, но ответы об использовании simpl XXX
здесь, похоже, не работают. Кажется, это более сложный случай.
2. Исходный веб-сайт предлагает доказательство того, что работает. Но доказательство кажется более сложным, чем необходимо, поскольку оно начинает анализ случая на условиях a1
и т. Д.
Case "APlus". destruct a1.
SCase "a1 = ANum n". destruct n.
SSCase "n = 0". simpl. apply IHa2.
SSCase "n ≠ 0". simpl. rewrite IHa2. reflexivity.
SCase "a1 = APlus a1_1 a1_2".
simpl. simpl in IHa1. rewrite IHa1.
rewrite IHa2. reflexivity.
SCase "a1 = AMinus a1_1 a1_2".
simpl. simpl in IHa1. rewrite IHa1.
rewrite IHa2. reflexivity.
SCase "a1 = AMult a1_1 a1_2".
simpl. simpl in IHa1. rewrite IHa1.
rewrite IHa2. reflexivity.
Итак, моя забота не в том, чтобы доказать эту простую теорему, а в том, как доказать это интуитивно, как на бумаге.
-- ОБНОВИТЬ --
Благодаря @gallais мой первоначальный план неверен, так как его можно изменить
aeval (optimize_0plus (APlus a1 a2))
to
aeval (APlus (optimize_0plus a1) (optimize_0plus a2))
только для случаев, когда a1
не ANum 0
. Случай 0
должен рассматриваться destruct a1.
отдельно, как и в случае с веб-сайтом курса, указанным в Примечании 2.
Однако у меня все еще есть тот же вопрос для других случаев, перечисленных ниже, и я думаю, что мой первоначальный план должен сработать:
5 subgoal
SCase := "a1 = APlus a1_1 a1_2" : String.string
Case := "APlus" : String.string
a1_1 : aexp
a1_2 : aexp
a2 : aexp
IHa1 : aeval (optimize_0plus (APlus a1_1 a1_2)) = aeval (APlus a1_1 a1_2)
IHa2 : aeval (optimize_0plus a2) = aeval a2
______________________________________(1/5)
aeval (optimize_0plus (APlus (APlus a1_1 a1_2) a2)) =
aeval (APlus (APlus a1_1 a1_2) a2)
...
______________________________________(5/5)
aeval (optimize_0plus (AMult a1 a2)) = aeval (AMult a1 a2)
Для каждого из этих 5 случаев кажется, что одно приложение (уменьшение beta
??) optimize_0plus
должно позволить нам внести изменения, например. (для AMinus
)
aeval (optimize_0plus (AMinus a1 a2))
to
aeval (AMinus (optimize_0plus a1) (optimize_0plus a2))
, правильно?
Если да, то как я могу сделать это одноступенчатое сокращение?
Примечание: я пробовал
Eval cbv beta in (aeval (optimize_0plus (AMinus a1 a2))).
И я даже не смог получить aeval (AMinus (optimize_0plus a1) (optimize_0plus a2))
, так как хотел бы использовать Eval
в доказательстве.