答案 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