在涉及序列的dafny代码中缺少不变式

时间:2019-01-19 07:22:56

标签: dafny

我想知道dafny无法验证我的程序的原因吗?

https://rise4fun.com/Dafny/Ip1s

我还缺少其他一些不变式吗?

1 个答案:

答案 0 :(得分:2)

问题在于您对s的定义和o的构造在“不同的方向”上进行。 s的递归情况在以下方面定义了s(i)i[0]以及s(i[1..])“先前”定义的内容。相反,循环迭代根据o和先前的i[n]定义了新的o。在您当前的程序中建立证明义务将需要归纳证明的引理,而Dafny本身并不是发明这样的引理。

对于此答案中的记录,这是您的开始:

function s(i: seq<int>): seq<int> {
  if |i| == 0 then [] else 
    if i[0] == 42 then [i[0]] + s(i[1..])
    else s(i[1..])
}

method q (i: seq<int>) returns (o: seq<int>)
  ensures o == s(i)
{
  var n := 0;
  o := [];

  while n < |i|
    invariant n <= |i| && o == s(i[..n])   
  {
    if i[n] == 42 {
      o := o + [i[n]];
    } 
    n := n + 1;
  } 
}

有四种出路。

一种解决方法是定义s的另一个版本,称为s',该版本从给定序列的另一端重复。然后,在方法说明中将s替换为s',并进行循环不变式。这是一个很好的解决方案,除非出于某种原因您真的在方法规范中更喜欢s,而不是s'

第二种解决方法是定义一个s'并证明s(i)s'(i)返回相同值的引理。这样一来,您就可以将s保留在方法规范中,而不必付出两个函数定义的麻烦,并且必须编写(证明和使用)引理。

第三种解决方法是更改​​循环以迭代“向下”而不是“向上”。也就是说,从n开始|i|,然后在循环主体中递减n。 (通常,n的增量通常最好在循环主体的末尾(后增量)完成,而n的减量通常最好在循环主体的末尾完成(递减)。

第四种解决方法是更改​​编写关于o的循环不变式的方式。当前,不变量谈到您已经计算出的内容,即o == s(i[..n])。您可以改为根据尚未计算的内容写不变量,如o + s(i[n..]) == s(i)所示,您可以将其读为“一旦我将s(i[n..])添加到o,我将得到{ {1}}”。这是s(i)的版本:

q

您可能也有兴趣观看this episode of Verification Corner这一主题。

Rustan