简体   繁体   English

为什么 Dafny 认为这个不正确的算法是正确的?

[英]Why is Dafny thinking that this incorrect algorithm is correct?

The following array reversing code is "proven correct" with Dafny but it clearly isn't correct.以下数组反转代码在 Dafny 中“被证明是正确的”,但它显然不正确。 What am I doing wrong?我究竟做错了什么?

A counter example is the array:一个反例是数组:

var a = new int[4] {1,3,5,7};

with the expected result {7,5,3,1} and the actual result {1,3,3,1} :预期结果{7,5,3,1}和实际结果{1,3,3,1}

// A method reversing an array

predicate is_sint32(x : int) { -0x8000_0000 <= x < 0x8000_0000 }

newtype {:nativeType "int"} sint32 = x | -0x8000_0000 <= x < 0x8000_0000

method reverse(a: array<sint32>)
    requires is_sint32(a.Length)
    modifies a;
    ensures forall i :: 0 <= i < a.Length ==> a[i] == old(a)[a.Length - 1 - i];
{
    var i       := 0 as sint32;
    var aLength := a.Length as sint32;

    while i < aLength
        invariant 0 <= i && i <= aLength
        invariant forall k :: 0 <= k < i ==> a[k] == old(a)[aLength - 1 - k];
    {
        a[aLength - 1 - i] := a[i];

        i := i + 1;
    }
}

I found a solution inspired by the great answer from James Wilcox.我找到了一个受 James Wilcox 出色回答启发的解决方案。 Adding here for future reference:在此处添加以供将来参考:

method reverse(a: array<int>)
    modifies a;
    ensures forall i :: 0 < i < a.Length ==> a[i] == old(a[a.Length - 1 - i]);
{
    var i := 0;

    while i < a.Length / 2
        invariant 0 <= i <= a.Length / 2
        invariant forall k :: 0 < k < i ==> a[k] == old(a[a.Length - 1 - k])
        invariant forall k :: i <= k < a.Length - i ==> a[k] == old(a[k])
        invariant forall k :: 0 <= k < i ==> a[a.Length - 1 - k] == old(a[k])
    {
        var v0 := a[i];
        var v1 := a[a.Length - 1 - i];

        a[i]                := v1;
        a[a.Length - 1 - i] := v0;

        i := i + 1;
    }
}

And here is the solution using C# native types for optimum performance when generating C# code:下面是使用 C# 本机类型在生成 C# 代码时获得最佳性能的解决方案:

predicate is_sint32(x : int) { -0x8000_0000 <= x < 0x8000_0000 }

newtype {:nativeType "int"} sint32 = x : int | -0x8000_0000 <= x < 0x8000_0000

method reverse(a: array<sint32>)
    requires is_sint32(a.Length)
    modifies a;
    ensures forall i :: 0 < i < a.Length ==> a[i] == old(a[a.Length - 1 - i]);
{
    var aLength     := a.Length as sint32;
    var aLengthDiv2 := aLength / 2;
    var i           := 0 as sint32;

    while i < aLengthDiv2
        invariant 0 <= i <= aLengthDiv2
        invariant forall k :: 0 < k < i ==> a[k] == old(a[aLength - 1 - k])
        invariant forall k :: i <= k < aLength - i ==> a[k] == old(a[k])
        invariant forall k :: 0 <= k < i ==> a[aLength - 1 - k] == old(a[k])
    {
        var v0 := a[i];
        var v1 := a[aLength - 1 - i];

        a[i]               := v1;
        a[aLength - 1 - i] := v0;

        i := i + 1;
    }
}

The problem is old(a)[...] does not mean what you think.问题是old(a)[...]并不代表你的想法。 See the reference manual section on old expressions .请参阅有关old表达式的参考手册部分

The short version is that old only affects "heap dereferences", which include field accesses (as in old(xf) ) and array accesses (as in old(a[i]) ).简短的版本是old影响“堆取消引用”,其中包括字段访问(如old(xf) )和数组访问(如old(a[i]) )。

Since your uses of old do not contain any field or array accesses inside the parentheses, they are not doing anything.由于您对old的使用在括号内不包含任何字段或数组访问,因此它们没有做任何事情。 Your postcondition is equivalent to:您的后置条件相当于:

ensures forall i :: 0 <= i < a.Length ==> a[i] == a[a.Length - 1 - i]

(simply deleting the old ), which says that after this method runs, a is guaranteed to be a palindrome. (只需删除old的 ),它表示在此方法运行后, a保证是回文。 Your code does satisfy this specification, but it's not the specification you intended to write.您的代码确实满足此规范,但它不是您打算编写的规范。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM