好的,所以这很奇怪,应该通过,但不是你想的那样。要理解为什么,我们首先要谈谈公平的“通常”定义,然后是技术定义。
弱公平说,如果一个动作从未被禁用,它最终会发生。您可能期望的是以下内容:
- 循环可以选择“结束”,但不会。
- 循环可以选择“结束”,但不会。这发生了任意次数。
- 由于循环是公平的*,它被迫选择“结束”。
- 我们完成了。
但事实并非如此。公平,在pluscal,是在标签级别。它没有说明标签中发生的事情,只是标签本身必须发生。所以这是一个完全有效的行为:
- 因为循环永远不会被禁用,它最终会发生。它选择“阅读”。
- 因为循环永远不会被禁用,它最终会发生。它选择“阅读”。
- 因为循环永远不会被禁用,它最终会发生。它选择“阅读”。
- 这一直持续下去。
这对应于您输入一个无限长的字符串,仅由 X 组成。如果你只想要有限的字符串,你必须明确地表达出来,要么作为规范的一部分,要么作为你想要的时间属性的先决条件之一。例如,您可以制作您的时间属性state = "end" ~> Termination
,您可以对输入字符串进行建模,您可以添加“y 之前有多少个 x”的计数,等等。不过,这会脱离实际错误并进入良好的规范设计。
这是正常情况。但这是该规则的一个非常非常具体的例外。那是因为“公平是如何定义的”。正式地,WF_vars(A) == <>[](Enabled <<A>>_v) => []<><<A>>_v
. 我们通常将其翻译为
如果系统总是能够做 A,那么它将继续做 A。
这就是我到现在为止使用的解释。但这在一个很大的方面是错误的。<<A>>_vars == A /\ (vars' /= vars)
,或“A发生并vars
改变。所以我们的定义实际上是
如果系统总是能够以改变其状态的方式执行 A ,那么它将继续以改变其状态的方式执行A。
一旦拥有pc = "Loop" /\ state = "reading"
,doingstate := "reading"
不会改变系统的状态,因此它不算作<<Next>>_vars
. 所以<<Next>>_vars
实际上并没有发生,但根据弱公平,它最终一定会发生。发生的唯一方法<<Next>>_vars
是如果循环设置state := "reading
,这允许 while 循环终止。
这是一个相当不稳定的情况。我们对规范所做的任何细微更改都可能将我们推回到更熟悉的领域。例如,以下规范不会像预期的那样终止:
variables
state = "start";
foo = TRUE;
begin
Loop:
while state /= "end" do
foo := ~foo;
either
\* we got an X, keep going
state := "reading"
or
\* we got a Y, terminate
state := "end"
end either;
end while;
end algorithm;*)
即使foo
不影响其余代码,它也让我们vars' /= vars
无需更新即可拥有state
. 同样,替换WF_vars(Next)
为WF_pc(Next)
会使规范失败,因为我们可以达到<<Next>>_pc
禁用状态(也称为任何状态state = "reading"
)。
*循环不公平,总算法是。这在某些情况下会产生很大的不同,这就是我们指定的原因。但在这种情况下,谈论 Loop 会更容易,因为它是唯一的动作。