以下程序导致assert v==40
违反断言:为什么?当数组a
只包含一个元素时,可以验证程序。
method Max(a:array<int>) returns(max:int)
requires 1<=a.Length
ensures forall j:int :: 0<=j< a.Length ==> max >= a[j]
ensures exists j:int :: 0<=j< a.Length && max == a[j]
{
max:=a[0];
var i :=1;
while(i < a.Length)
invariant 1<=i<=a.Length
decreases a.Length-i
invariant forall j:int :: 0<=j<i ==> max >= a[j]
invariant exists j:int :: 0<=j<i && max == a[j]
{
if(a[i] >= max){max := a[i];}
i := i + 1;
}
}
method Test(){
var a := new int[2];
a[0],a[1] := 40,10;
var v:int:=Max(a);
assert v==40;
}
答案 0 :(得分:1)
让我们从人类层面证明断言实际上是有效的。从Max
的后置条件,我们知道关于v
的两件事:(1)它至少与a
中的每个元素一样大,(2)它等于某个元素a
。通过(2),v
是40或10,而按(1),v
至少为40(因为它至少与a[0]
一样大,是40)。由于10不是至少40,v
不能是10,所以它必须是40。
现在,为什么Dafny不能自动理解这一点?这是因为(1)中的forall
量词。 Dafny(真的是Z3)在内部使用&#34;触发器&#34;近似通用量词的行为。 (没有任何近似值,量词的推理通常是不可判定的,因此需要这样的限制。)触发工作的方式是对于程序中的每个量词,推断出一种称为触发器的句法模式。然后,除非触发器匹配上下文中的某个表达式,否则完全忽略该量词。
在此示例中,fact(1)将触发a[j]
。 (您可以通过将鼠标悬停在量词上来查看在Visual Studio或VSCode或emacs中推断出的触发器。或者在命令行上,通过传递选项/printTooltips
并查找行号。)这意味着量词将会除非在上下文中对于任何表达式a[foo]
都有foo
形式的表达式,否则将被忽略。然后(1)将使用foo
j
进行实例化,我们将学习max >= a[foo]
。
由于您的Test
方法断言未提及a[foo]
形式的任何表达式,因此Dafny将无法使用事实(1),从而导致虚假断言违规。
修复Test
方法的一种方法是添加断言
assert v >= a[0];
就在另一个断言之前。这是我们在人类水平证明中需要的事实(1)的关键结果,它包含与触发器匹配的表达式a[0]
,允许Dafny实例化量词。然后证据的其余部分会自动完成。
有关一般触发器以及如何手动编写触发器的详细信息,请参阅this answer。