Arrays 方法前提条件在对数组中的方法值唯一性进行4次调用后失败
我正在尝试编写一个dafny程序,它有一个固定大小的数组。如果此数组尚未填充,并且正在添加的值在数组中不存在,则可以通过方法将其添加到。起初它似乎运行良好,但是,当我调用该方法超过4次时,我得到一个错误Arrays 方法前提条件在对数组中的方法值唯一性进行4次调用后失败,arrays,dafny,Arrays,Dafny,我正在尝试编写一个dafny程序,它有一个固定大小的数组。如果此数组尚未填充,并且正在添加的值在数组中不存在,则可以通过方法将其添加到。起初它似乎运行良好,但是,当我调用该方法超过4次时,我得到一个错误 SimpleTest.dfy(37,15): Error: A precondition for this call might not hold. SimpleTest.dfy(17,23): Related location: This is the precondition that mi
SimpleTest.dfy(37,15): Error: A precondition for this call might not hold.
SimpleTest.dfy(17,23): Related location: This is the precondition that might not hold.
Execution trace:
(0,0): anon0
这突出显示了行需要x!在以下MCVE的arr[…]
中
为什么先决条件在方法被调用超过四次后开始失败?无论阵列的固定大小有多大,这种情况似乎都会发生
class {:autocontracts} Test
{
var arr: array<nat>;
var count: nat;
constructor(maxArrSize: nat)
requires maxArrSize > 1
ensures count == 0
ensures arr.Length == maxArrSize
ensures forall i :: 0 <= i < arr.Length ==> arr[i] == 0
{
arr := new nat[maxArrSize](_ => 0);
count := 0;
}
method AddIn(x: nat)
requires x !in arr[..]
requires x > 0
requires 0 < arr.Length
requires count < arr.Length
ensures arr[..] == old(arr[.. count]) + [x] + old(arr[count + 1 ..])
ensures count == old(count) + 1
ensures arr == old(arr)
{
arr[count] := x;
count := count + 1;
}
}
method Main()
{
var t := new Test(20);
t.AddIn(345);
t.AddIn(654);
t.AddIn(542);
t.AddIn(56);
t.AddIn(76);
t.AddIn(8786);
print t.arr[..];
print "\n";
print t.count;
print " / ";
print t.arr.Length;
print "\n";
}
class{:autocontracts}测试
{
var-arr:数组;
var计数:nat;
构造函数(maxArrSize:nat)
需要maxArrSize>1
确保计数=0
确保阵列长度==最大阵列大小
确保所有i::0 arr[i]==0
{
arr:=新nat[maxArrSize](=>0);
计数:=0;
}
方法AddIn(x:nat)
在arr[…]中需要x
需要x>0
要求0<阵列长度
要求计数<阵列长度
确保arr[…]==old(arr[…count])+[x]+old(arr[count+1..)
确保计数==旧(计数)+1
确保arr==旧(arr)
{
arr[count]:=x;
计数:=计数+1;
}
}
方法Main()
{
var t:=新试验(20);
t、 艾丁(345);
t、 艾丁(654);
t、 艾丁(542);
t、 艾丁(56);
t、 艾丁(76);
t、 艾丁(8786);
打印t.arr[…];
打印“\n”;
打印t.count;
打印“/”;
打印t.arr.Length;
打印“\n”;
}
为了证明该方法的先决条件,验证器必须经历许多情况,每个情况都使用堆、序列和构造函数后置条件中的量词的属性。这些情况似乎耗尽了验证器的一些限制,因此您得到了一个错误,即它无法证明某些东西
您可以通过编写一些断言来帮助验证者。这些断言还将确认您自己对程序正在构建的程序状态的理解。例如,如果添加这些assert
语句,验证器将确认断言并能够证明整个程序
method Main()
{
var t := new Test(20);
assert t.arr[..] == seq(20, _ => 0);
t.AddIn(345);
assert t.arr[..] == [345] + seq(19, _ => 0);
t.AddIn(654);
assert t.arr[..] == [345, 654] + seq(18, _ => 0);
t.AddIn(542);
assert t.arr[..] == [345, 654, 542] + seq(17, _ => 0);
t.AddIn(56);
assert t.arr[..] == [345, 654, 542, 56] + seq(16, _ => 0);
t.AddIn(76);
assert t.arr[..] == [345, 654, 542, 56, 76] + seq(15, _ => 0);
t.AddIn(8786);
assert t.arr[..] == [345, 654, 542, 56, 76, 8786] + seq(14, _ => 0);
print t.arr[..];
print "\n";
print t.count;
print " / ";
print t.arr.Length;
print "\n";
}
您不需要所有这些断言,但我将它们留在这里,以便您可以看到一般形式
使用这些附加断言进行验证的原因是,每个断言都可以相当容易地从前面的断言中得到证明。因此,附加的断言会使验证者更快地找到证据(尤其是在验证者放弃之前找到证据)
顺便说一句,您对Test
类的说明揭示了Test
对象的内部表示。如果可以对调用者隐藏这些细节,通常会得到更多的信息隐藏、更好的规范以及更好的验证程序性能。要做到这一点,您需要添加一个对象不变量(按照习惯在Dafny中编码为Valid()
谓词)。我有很多要解释的。但是我看到您已经在使用{:autocontracts}
,所以您可能知道一些关于这方面的事情。因此,在不作进一步解释的情况下,下面是您的类的规范在更抽象的形式中的样子
class {:autocontracts} Test
{
// The public view of the Test object is described by the following two fields:
ghost var Contents: seq<nat>
ghost var MaxSize: nat
// The private implementation of the Test object is given in terms of the
// following fields. These fields are never mentioned in pre/post specifications.
var arr: array<nat>
var count: nat
predicate Valid() {
count <= arr.Length == MaxSize &&
Contents == arr[..count]
}
constructor(maxArrSize: nat)
ensures Contents == [] && MaxSize == maxArrSize
{
arr := new nat[maxArrSize];
Contents, MaxSize, count := [], maxArrSize, 0;
}
method AddIn(x: nat)
requires x !in Contents
requires |Contents| < MaxSize
ensures Contents == old(Contents) + [x] && MaxSize == old(MaxSize)
{
arr[count], count := x, count + 1;
Contents := Contents + [x];
}
}
method Main()
{
var t := new Test(20);
t.AddIn(345);
t.AddIn(654);
t.AddIn(542);
t.AddIn(56);
t.AddIn(76);
t.AddIn(8786);
print t.arr[..], "\n";
print t.count, " / ", t.arr.Length, "\n";
}
class{:autocontracts}测试
{
//测试对象的公共视图由以下两个字段描述:
重影变量内容:seq
ghost var MaxSize:nat
//测试对象的私有实现是根据
//以下字段。这些字段在前/后规范中从未提及。
var-arr:数组
变量计数:nat
谓词有效(){
谢谢你的回答。我知道Valid
谓词,它实际上在我的主程序中使用,只是没有用于这一部分。虽然我不知道ghost-var,所以这真的很有用。