我想在dafny中证明以下内容:
function append(xs: seq<int>) : seq<int> {
if |xs| == 0 then []
else [xs[0]] + append(xs[1..])
}
method test(o:seq<int>, xs: seq<int>, i:int)
requires 0 <= i < |xs|
{
if o == append(xs[..i])
{
assert o + [xs[i]] == append(xs[..(i+1)]);
}
}
我认为这需要使用引理写一个归纳证明,但是我不确定如何写引理。 online doc给出了对序列内容进行结构归纳的示例,但我认为在这种情况下,我认为归纳步骤需要在i
上进行?我试着写一个如下:
lemma appendLemma (xs:seq<int>, o:seq<int>, i:int)
requires 0 <= i < |xs|
requires o == append(xs[..i])
ensures o + [xs[i]] == append(xs[..(i+1)])
{
if i == 0
{
assert o + [xs[0]] == append(xs[..1]);
}
else
{
appendLemma(xs, o, i);
// what to do here?
}
}
但是它一直在要求一个减少子句,在这种情况下我不确定是否有一个?
答案 0 :(得分:0)
您将对序列的长度(xs和o)进行归纳,然后我也必须减小,如您在下面的证明中所见。所以你可以说你对我做了归纳。但是无论您做什么,对appendLemma 的内部调用都必须在较小的参数上。它代表归纳假设,请参见。这就是Dafny抱怨解雇的原因。
由于我们对函数append的了解仅是其归纳定义,因此以头/尾方式,我们必须在appendLemma的证明中遵循该方式,并在xs的尾部应用归纳。
我需要一个额外的引理(appendIsCopyLemma
),以捕捉Levent Erkok的观点,该附录实际上是一个简单的副本。请注意,Dafny无需进一步帮助即可验证该引理,但您必须在appendLemma的证明中明确调用它(两次)。
请注意,Dafny现在将自己推断出Dafny在原始代码中缺少的减少子句;递归调用的参数现在已经足够了。
// Verified by Dafny 2.2.0
// definition of append and test in the question above
lemma appendIsCopyLemma(xs: seq<int>)
ensures xs == append(xs);
{ }
lemma appendLemma (xs:seq<int>, o:seq<int>, i:int)
// decreases i; // Dafny can infer a decrease clause by itself
requires 0 <= i < |xs|
requires o == append(xs[..i])
ensures o + [xs[i]] == append(xs[..(i+1)])
{
if |xs| == 1 || i == 0
{
assert o + [xs[0]] == append(xs[..1]); //redundant
}
else
{
appendIsCopyLemma(xs[..i]);
assert o[1..] == xs[1..][..i-1];
appendLemma(xs[1..], o[1..], i-1);
appendIsCopyLemma(xs[..i+1]);
assert xs[..i+1] == append(xs[..i+1]);
}
}