method Main(a : array<int>)
requires a != null;
requires a.Length > 1;
modifies a;
{
var t := a[0]; a[0] := a[1]; a[1] := t;
}
Нам важно, что сумма элементов при этом не изменилась. Записываем соответствующее постусловие и функцию вычисления суммы:
function sum(c : seq<int>) : int
{ if |c| == 0 then 0 else c[0] + sum(c[1..]) }
method Main(a : array<int>)
requires a != null;
requires a.Length > 1;
ensures sum(a[..]) == sum(old(a[..]));
modifies a;
{
var t := a[0]; a[0] := a[1]; a[1] := t;
}
На это Dafny говорит: A postcondition might not hold on this return path. И показывает на равенство sum. Ну ладно, вобщем-то это и непросто было бы автоматически доказать. Пишем теорему!
function sum(c : seq<int>) : int
{ if |c| == 0 then 0 else c[0] + sum(c[1..]) }
ghost method theorem(c : seq<int>, c1 : seq<int>)
requires |c1| > 1;
requires |c| == |c1|;
requires c[0] == c1[1];
requires c1[0] == c[1];
requires c[2..] == c1[2..];
ensures sum(c) == sum(c1);
{
// ...
}
method Main(a : array<int>)
requires a != null;
requires a.Length > 1;
ensures sum(a[..]) == sum(old(a[..]));
modifies a;
{
ghost var c1 := a[..];
var t := a[0]; a[0] := a[1]; a[1] := t;
ghost var c3 := a[..];
theorem(c3, c1);
}
Пока доказательство теоремы нас не интересует, поэтому его тут нет. Ну, если уж мы сами покажем, как доказывать теорему, метод Main должен быть верным. Спрашиваем Dafny: A postcondition might not hold on this return path ! Как будто Dafny не видит нашу теорему! Ну-ка, добавим после нее ассершн, который теорема должна доказать:
function sum(c : seq<int>) : int
{ if |c| == 0 then 0 else c[0] + sum(c[1..]) }
ghost method theorem(c : seq<int>, c1 : seq<int>)
requires |c1| > 1;
requires |c| == |c1|;
requires c[0] == c1[1];
requires c1[0] == c[1];
requires c[2..] == c1[2..];
ensures sum(c) == sum(c1);
{
// ...
}
method Main(a : array<int>)
requires a != null;
requires a.Length > 1;
ensures sum(a[..]) == sum(old(a[..]));
modifies a;
{
ghost var c1 := a[..];
var t := a[0]; a[0] := a[1]; a[1] := t;
ghost var c3 := a[..];
theorem(c3, c1);
assert sum(c3) == sum(c1);
assert sum(c1) == sum(old(a[..]));
assert sum(c3) == sum(a[..]);
}
На это получаем: assertion violation! Dafny не верит в то, что в конце метода sum(c3) == sum(a[..]), но ведь до метода было c3 == a[..], после этого содержимое массива не менялось, a[..] тоже не меняет значение после перестановки 0'го и 1'го элемента. Как же побороть assertion violation ?
А побороть его не так сложно. Надо явно сказать, что в теореме a[..] не меняется. Для этого нам нужно передать в теорему сам массив (иначе мы не сможем выразить "неизменчивость") и сформулировать в нем неизменчивость:
function sum(c : seq<int>) : int
{ if |c| == 0 then 0 else c[0] + sum(c[1..]) }
ghost method theorem(a : array<int>, c : seq<int>, c1 : seq<int>)
requires |c1| > 1;
requires |c| == |c1|;
requires c[0] == c1[1];
requires c1[0] == c[1];
requires c[2..] == c1[2..];
ensures sum(c) == sum(c1);
requires a != null;
ensures a[..] == old(a[..]);
{
// ...
}
method Main(a : array<int>)
requires a != null;
requires a.Length > 1;
ensures sum(a[..]) == sum(old(a[..]));
modifies a;
{
ghost var c1 := a[..];
var t := a[0]; a[0] := a[1]; a[1] := t;
ghost var c3 := a[..];
theorem(a, c3, c1);
assert sum(c3) == sum(c1);
assert sum(c1) == sum(old(a[..]));
assert sum(c3) == sum(a[..]);
}
После этого assertion violation исчезнет :)
Комментариев нет:
Отправить комментарий