在dafny中编写归纳引理

时间:2019-02-05 03:18:06

标签: z3 dafny

我想在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?
  }
}

但是它一直在要求一个减少子句,在这种情况下我不确定是否有一个?

1 个答案:

答案 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]);
  }
}