在Hoare风格的正确性证明中如何处理数组


11

在围绕该问题的讨论中,Gilles正确地提到,使用数组的算法的任何正确性证明都必须证明没有越界的数组访问;根据运行时模型,这将导致运行时错误或访问非数组元素。

一种执行这种正确性证明的通用技术(至少在本科生中,可能在自动验证中)是使用Hoare逻辑。我不知道标准规则集包含与数组有关的任何内容;他们似乎仅限于单子变量。

我可以想象添加以下形式的公理

{0i<A.lengthP[A[i]/E]} A[i]:=E; {P}

但是,我不清楚您将如何处理右侧的数组访问,即在某些语句x:= E中它是否是复杂表达式E的一部分。x:=E

如何在Hoare逻辑中对数组访问进行建模,以便可以并且必须证明无效访问的存在以确保程序的正确性?

答案可能假设我们不允许数组元素比其他语句中使用或一些的一部分在,因为这不限制表现; 我们总是可以为临时变量分配所需的值,即写而不是。A[i]:=EEx:=Et:=A[i]; if(t>0)if(A[i]>0)

Answers:


8

您的公理不是真正的公理,它缺少假设。Hoare逻辑的简单表示形式为形式的公式,其中和是逻辑公式,是命令。您确实需要确保的格式正确。在简单的语言(例如经常用于首次介绍Hoare逻辑的语言)中,格式良好是语法上的问题:通常是检查{P}C{P}PPCCC符合上下文无关文法,并且可能自由变量在允许的集合内。如果该语言包含具有语义正确性的构造,例如对数组元素的访问,则需要添加假设以表达这种语义正确性。

形式上,您可以添加判断来表达表达式和命令的更正。如果表达式没有副作用,则它们不需要后置条件,只需要前置条件。例如,您可以编写格式正确的规则,例如 并且只允许在命令中使用格式正确的表达式:

{P}E wf{P0E<length(A)}A[E] wf{P}E1 wf{P}E2 wf{P}E1+E2 wf
{P[xE]}E wf{P[xE]}x:=E{P}

另一种方法是将所有表达式视为格式正确,但使任何包含格式计算的表达式都具有特殊值。在具有运行时边界检查的语言中,表示“此程序引发了致命异常”。然后,您将通过逻辑谓词跟踪程序是否;仅当您可以证明其后置条件暗示,该程序才有效。 errorerrorError¬Error

{P[xE]}x:=E{PError}P[xE]Eerror{P[xE]}x:=E{P}

另一种方法是仅在程序正确终止时考虑保留Hoare三元组。这是非终止程序的常用方法:命令终止时,后置条件成立,这可能并不总是发生。如果您将运行时错误视为非终止错误,那么您将彻底清除所有正确性问题。您仍将需要以某种方式证明程序的正确性,但是如果您更喜欢其他形式化的方法来完成该任务,则不必遵循Hoare逻辑。

顺便说一句,请注意,表达修改复合变量(例如数组)时发生的事情比编写的要复杂得多。假设是:替换不会更改,但是赋值可能会使无效。即使将谓词的语法限制为仅谈论原子,也要在条件下考虑赋值:您无法简单地替换以获得正确的后置条件,则需要求值PIsSorted(A)A[i]EPA[i]PPA[A[0]1]:=A[0]A[0]=2A[1]=3A[0]=1A[1]=1A[0](这通常很难,因为前提条件可能没有为指定单个可能的值)。您需要对数组本身执行替换:。Mike Gordon的讲义很好地介绍了带有数组的Hoare逻辑(但没有错误检查)。A[0]AA[iE]


0

正如Gilles提到的,有一个数组分配公理(请参阅Gordon的说明,第2.1.10节): 换句话说,如果您有数组分配,则用位置值为的数组替换原始数组。请注意,如果您已经在帖子中分配了,那么您应该以pre为准(是的,按此顺序-首先执行最新更新!)。

{Q[AA.store(i,expr)]}A[i]=expr{Q}
A.store(i,expr)iexprA.store(i,vi)A[j]=vjA.store(j,vj).store(i,vi)

另外,我们需要数组访问公理:A.store(i,v)[i]可以替换为v(“如果访问刚刚更新的第个元素,则返回分配的值”)。i

我认为,为了证明带有数组的程序是正确的(“无界外访问”),上述公理就足够了。让我们考虑一下程序:

...
A[i] = 12
...

我们将对该程序进行注释:

...
@ {0<i<A_length}
A[i] = 12
...

其中A_length的一个变量指定数组的长度。现在尝试证明注释-即,将其倒退(从下到上,在Hoare证明中“通常”)。如果您首先获得{false},则可能发生越界访问,否则,获得的表达式是不可能进行越界访问的前提。(同样,我们需要确保在创建数组时像int A=int[10];在后置条件中那样{A_length==10})。


您的公理不会模拟越界访问:他们甚至没有提到长度!在您的示例程序,你怎么涉及lengthA
吉尔(Gilles)'所以

是的,公理不会建模超出范围的访问。首先,为了证明程序是正确的,我添加了要求访问在范围之内的注释。(length重命名A_length。)其次,我们需要像这样的数组“创建”公理int[] a = int[length] {a_length==length}。我认为这应该足够了。
Ayrat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.