Чтобы доказать существование, вам часто нужно предоставить свидетеля. Чтобы сделать это для утверждения, добавьте еще одно утверждение со свидетелем перед ним. Например:
assert missing[0] == 0;
assert exists k :: 0 <= k < missing.Length && missing[k] == 0;
Для экзистенциального квантификатора в инварианте l oop я предлагаю вам ввести еще одну переменную для хранения свидетеля. Если эта переменная используется только в доказательстве, вы можете сделать ее призраком. Ваш код будет выглядеть примерно так:
ghost var indexOfMissing := 0;
i := 0;
while i < a.Length
...
invariant 0 <= indexOfMissing < missing.Length && missing[indexOfMissing] == 0
Вам придется вручную обновить indexOfMissing
внутри l oop, чтобы сохранить инвариант.
Вот еще один момент о логи c и два замечания о Дафни:
Вы начали с того, что forall
подразумевает exists
. Это не так, если диапазон квантификаторов пуст. К счастью, в вашем случае у вас есть missing.Length != 0
.
Ваш первый l oop инициализирует элементы от missing
до 0
. Есть два простых способа сделать это в Дафни. Одним из них является использование агрегатного оператора, который выполняет несколько одновременных присваиваний:
forall i | 0 <= i < missing.Length {
missing[i] := 0;
}
Другой - дать new
функцию, которая говорит, как инициализировать элементы.
var missing := new int[n](i => 0);
Преимущество обоих этих методов в том, что они не являются циклами, а это означает, что вам не нужно поддерживать индекс al oop и записывать различные инварианты l oop.
Вы также можете исключить окончательный l oop, если используете оператор присвоения такого:
m :| 0 <= m < missing.Length && missing[m] == 0;