什么是不变式,如何使用它们,您是否曾经在程序中使用过它?


48

我正在阅读《工作中的编码器》,其中有很多关于不变式的讨论。据我了解,不变量是在表达式之前和之后均保持的条件。如果我正确记得我的逻辑课程,那么它们对于证明循环正确是很有用的。

我的描述是正确的,还是我错过了什么?您在程序中使用过它们吗?如果是这样,他们如何受益?



@罗伯特·哈维:是的,我实际上读过。但是在我看来,不变量仅在您尝试证明某些东西时才有用。这是正确的吗?
gablin

这是我的理解;当您尝试对程序进行推理时,请证明其正确性。
罗伯特·哈维

3
@ user9094:断言是在运行时的特定点声明某些内容正确的声明,并在代码中表示。不变式是一条语句(希望有充分的基础),只要适用,就始终为真,并且不会在代码本身中表示。
David Thornley 2010年

1
不变性确实对于证明正确性很有用,但不限于此。它们对于防御性编程和调试期间也很有用。他们不仅帮助证明您的代码是正确的,还帮助推理代码并找到错误的出处。
奇怪的想

Answers:


41

在OOP中,不变量是一组声明,它们必须在对象的生存期内始终成立,以使程序有效。每当对象当前未执行更改其状态的方法时,从构造函数的末尾到析构函数的开头,它均应为true。

不变的一个例子可能是两个成员变量之一恰好为空。或者说,如果一个具有给定值,那么另一个允许的值就是这个或那个...

我有时使用对象的成员函数检查不变量是否成立。如果不是这种情况,则会引发一个断言。并且该方法在更改对象的每个方法的开始和退出处调用(在C ++中,这仅是一行...)


11
提及不变式的+1不必在执行方法的中间为true。
奇怪的想

1
@Oddthinking尽最大可能避免这种情况。容易进入一个不变的状态,而忘记在返回之前正确地还原所有内容。异常也会给您带来麻烦。
亚历山大

3
@亚历山大:对于非平凡的不变式,几乎是不可避免的。如果您需要在一种方法中更新多个变量(如答案中所述),则有一点只能更新一个,并且不变式为false。在编写好的代码而不添加新代码方面有足够的限制。
奇怪的想'18

@Oddthinking是的,这通常是不可避免的。但是,例如,如果存在一组逻辑上属于同一变量(例如,数组和数组中“选定”项的索引),则可能值得将它们提取为类型。从那里,数组或类型的突变可以表示为该类型新实例的单次分配
Alexander

13

好吧,我在该线程中看到的东西都很棒,但是我有一个“不变式”的定义,这对我的工作非常有帮助。

不变性是程序执行期间必须遵守的任何逻辑规则,可以传达给人类,但不能传达给编译器。

此定义很有用,因为它将条件分为两类:可以信任编译器的强制执行条件,以及必须记录,讨论,评论或以其他方式传达给贡献者的条件,以便他们与代码库进行交互而不会引入错误。

另外,此定义很有用,因为它允许您使用概括性“不变式不好”。

例如,手动变速箱中的换挡器经过精心设计,可以避免产生不变量。如果需要的话,我可以为每个档建立一个变速杆。该杠杆可以向前(“接合”)或向后(“分离”)。在这样的系统中,我创建了一个“不变式”,可以这样记录:

“至关重要的是,在接合不同的齿轮之前,先将当前接合的齿轮断开。要同时接合任何两个齿轮,将导致机械应力,从而将变速器撕裂。在接合另一个齿轮之前,请始终分离当前接合的齿轮。”

因此,有人可能将变速箱损坏归咎于马虎驾驶。但是,现代汽车只使用一根可在齿轮之间旋转的棍棒。它的设计方式使现代变速杆汽车无法同时接合两个齿轮。

通过这种方式,我们可以说变速箱已被设计为“删除不变式”,因为它不允许以违反逻辑规则的方式对其进行机械配置。

从代码中删除的每种此类不变式都是一种改进,因为它降低了使用它的认知负担。


1
如果不变量是在程序执行过程中必须遵守的任何逻辑规则,并且逻辑规​​则是不允许同时啮合两个齿轮,那么也不是不变的是不能同时啮合两个齿轮时间?如果没有这种不变性,您的变速箱可能会同时处于两个齿轮中,从而使其自身断裂。首先,难道不是单个变速杆实际上强制执行了该不变式吗?第二,为什么不变式本质上是好还是坏?
达斯汀·克里夫兰

1
对汽车齿轮的比较对我来说很清楚。谢谢!
Marecky

“不变性是程序执行期间必须遵守的任何逻辑规则,可以传达给人类,但不能传达给编译器。” -我很喜欢,简洁明了,易于记忆。
ZeroKnight,

@DustinCleveland我认为在此示例中,摇杆移动背后的机制是“强制执行”规则的“编译器”,而否则可能导致事件发生的驱动程序是必须消耗并记住信息的众多客户端之一。被“记录,讨论,评论或以其他方式传达”。
ebernard

精彩的解释!我现在真的很明白为什么在代码中使用不变式是不好的做法的原因。
Ben C Wang

3

不变(常识)表示某些条件必须在某个时间点甚至在程序执行时始终为真。例如,PreConditions和PostConditions可用于断言某些条件,这些条件在调用函数和返回函数时必须为真。对象不变式可用于断言对象在存在期间始终必须具有有效状态。这就是按合同设计的原则。
我在代码中通过检查非正式地使用了不变量。但是最近,我正在使用直接支持不变式的.Net代码协定库


3

基于以下来自Coders At Work的报价...

但是,一旦您知道它保持不变,就可以看到啊,如果我们保持不变,那么我们将获得日志查找时间。

...我想“ invariant” =“您要维持以确保达到预期效果的条件”。

似乎不变式有两种微妙的不同含义:

  1. 保持不变的东西。
  2. 为了达到目标X,您要尝试保持相同的值(例如上面的“日志查找时间”)。

所以1就像一个断言;我认为2就像是证明正确性,性能或其他属性的工具。有关2的示例,请参阅Wikipedia文章(证明MU拼图解决方案的正确性)。

实际上,第三种不变性是:

.3。程序(或模块或功能)应该做什么?换句话说,它的目的。

来自同一位“工作中的程序员”访谈:

但是,使大型软件易于管理的原因是,它具有一些全局不变性或关于其应做的事情以及应做的事情是真实的大体陈述。


1

不变性就像可以用来支配程序逻辑的规则或假设。

例如,假设您有一些跟踪用户帐户的软件应用程序。还假设用户可以有多个帐户,但是出于任何原因,您都需要区分用户的主帐户和“别名”帐户。

这可能是数据库记录或其他记录,但是现在让我们假设每个用户帐户都由一个类对象表示。

class userAccount {private char * pUserName; 私人字符* pParentAccountUserName;

...}

不变的假设是,如果pParentAccountUserName为NULL或为空,则此对象为父帐户。您可以使用该变量来区分不同类型的帐户。可能有更好的方法来区分不同类型的用户帐户,因此请记住,这只是一个示例,说明了如何使用不变式。


不变式检查程序的状态。它们不是设计决策。
Xavier Nodet 2010年

3
不变式不检查任何内容。您可以检查程序的状态,以查看不变式是TRUE还是FALSE,但是不变式本身“什么也不做”。
Pemdas 2010年

2
通常,在C ++中,您会看到某种类不变性,例如成员x必须小于25且大于0。这就是不变性。任何针对该不变式的检查都是断言。在上面的示例中,我的不变式是如果pParentAccountUserName为NULL或为空,则它是一个父帐户。不变性是设计决定。
Pemdas 2010年

如何检查如果pParentAccountUserName为NULL或为空,则此对象是父帐户?您的语句仅定义了应该代表空值/空值的内容。不变的是系统符合该要求,即pParentAccountUserName如果是父帐户,则只能为null或为空。这是一个微妙的区别。
卡梅伦

1

从物理学的背景来看,在物理学中,我们有不变量,它们本质上是数量,在整个计算/模拟过程中不会变化。例如,在物理学中,对于一个封闭的系统,总能量是守恒的。还是在物理学上,如果两个粒子碰撞,则产生的碎片必须完全包含它们开始时的能量以及完全相同的动量(矢量)。通常,没有足够的不变式来完全指定结果。例如,在2粒子碰撞中,我们有四个不变量,三个动量分量和一个能量分量,但是系统具有六个自由度(六个数字来描述其状态)。不变量应在舍入误差内保守,但其保守性不能证明解是正确的。

因此,通常情况下,这些内容作为健全性检查很重要,但它们本身无法证明正确性。


1
-1物理不变性是不同的。计算解决方案与证明算法正确无异。对于后者,不变量可以证明正确性。
aaronasterling 2010年
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.