Вы уже доказали это! Вы написали нужное свойство в качестве постусловия функции, и Дафни проверяет его без нареканий. Вот и все.
Вы также можете использовать лемму, чтобы доказать свойство. Вот пример:
function count(a: seq<bool>): nat
{
if |a| == 0 then 0 else
(if a[0] then 1 else 0) + count(a[1..])
}
lemma CountProperty(a: seq<bool>)
ensures count(a) <= |a|
{
}
Опять же, Дафни проверяет лемму, не предъявляя никаких претензий, так что вы ее доказали!
Неверно предполагать, что Дафни всегда будет доказывать вам что-то автоматически. Поэтому неплохо научиться писать доказательства вручную. Вот ручное доказательство этого свойства. Просто чтобы убедиться, что Дафни не пытается выполнять индукцию автоматически, я использовал директиву, чтобы отключить ее (что сделало нашу жизнь сложнее, чем обычно делала бы Дафни):
lemma {:induction false} CountProperty(a: seq<bool>)
ensures count(a) <= |a|
{
// Let's consider two cases, just like the definition of "count"
// considers two cases.
if |a| == 0 {
// In this case, we have:
assert count(a) == 0 && |a| == 0;
// so the postcondition follows easily.
} else {
// By the definition of "count", we have:
assert count(a) == (if a[0] then 1 else 0) + count(a[1..]);
// We know an upper bound on the first term of the addition:
assert (if a[0] then 1 else 0) <= 1;
// We can also obtain an upper bound on the second term by
// calling the lemma recursively. We do that here:
CountProperty(a[1..]);
// The call we just did gives us the following property:
assert count(a[1..]) <= |a[1..]|;
// Putting these two upper bounds together, we have:
assert count(a) <= 1 + |a[1..]|;
// We're almost done. We just need to relate |a[1..]| to |a|.
// This is easy:
assert |a[1..]| == |a| - 1;
// By the last two assertions, we now have:
assert count(a) <= 1 + |a| - 1;
// which is the postcondition we have to prove.
}
}
Более приятный способ написать такое доказательство — использовать проверенное вычисление, которое Дафни называет «выражением вычисления»:
lemma {:induction false} CountProperty(a: seq<bool>)
ensures count(a) <= |a|
{
if |a| == 0 {
// trivial
} else {
calc {
count(a);
== // def. count
(if a[0] then 1 else 0) + count(a[1..]);
<= // left term is bounded by 1
1 + count(a[1..]);
<= { CountProperty(a[1..]); } // induction hypothesis gives a bound for the right term
1 + |a[1..]|;
== { assert |a[1..]| == |a| - 1; }
|a|;
}
}
}
Я надеюсь, что это поможет вам начать.
Программируйте безопасно,
Рустан
person
Rustan Leino
schedule
28.02.2017