ASSERT 0
(第22行),为什么所有的断言都起作用?function CountFactors(i:nat): nat
requires i >= 1;
{
var a := set b | 1 <= b <= i && i % b == 0;
|a|
}
function CountFactorsSet(i:nat): set<nat>
requires i >= 1;
{
var a := set b | 1 <= b <= i && i % b == 0;
a
}
method CountFactorsMethod(i:nat) returns (a: set<nat>)
requires i >= 1;
{
a := set b | 1 <= b <= i && i % b == 0;
}
method Main()
{
var r:= CountFactorsMethod(2);
print(r);
// assert CountFactorsSet(2) == {1, 2}; // ASSERT 0
assert CountFactors(2) == 2; // ASSERT 1
}
这里是link to the code。我正在使用Dafny 2.3.0.10506
答案 0 :(得分:2)
在Dafny中使用集(或图或序列)时,这是一个非常常见的问题。这个问题归结为所谓的集合的“扩展相等”。
在数学上,如果两组元素相同,则它们相等。也就是说,(使用伪Dafny语法):
A == B <==> (forall x :: x in A <==> x in B)
这为证明两步相等的集合提供了非常强大的推理原理。取A
的任意元素并显示在B
中,然后取B
的任意元素并显示在A
中。
不幸的是,从自动推理的角度来看,这是一项相当昂贵的任务。如果Dafny试图通过该规则证明每个集合与它可以想到的每个其他集合相等,那就太慢了。
Dafny遵循以下经验法则:
除非达芬妮在程序中断言它们相等,否则我将不会尝试通过扩展性证明两个集合相等。
这通过限制Dafny考虑证明的套数彼此相等来保持验证性能。
因此,当您assert CountFactorsSet(2) == {1, 2};
时,您授予Dafny权限以关于集CountFactorsSet(2)
的扩展推理,事实证明足以解决此问题。
顺便说一句,在大型程序中,如果您从不重复两次设定的理解,那么您的运气会更好。取而代之的是,总是像这样将理解包在函数中。
function CountFactorsSet(i:nat): set<nat>
requires i >= 1;
{
set b | 1 <= b <= i && i % b == 0
}
function CountFactors(i:nat): nat
requires i >= 1;
{
|CountFactorsSet(i)|
}
通过使用函数而不是直接理解,可以使Dafny的生活变得更加轻松,因为对应用到同一参数的函数的两次出现相等是“显而易见的”,而对Dafny而言,这两次出现并不“显而易见”相同理解的不同句法出现是相等的。
事实证明,在您的示例中这无关紧要,但我只是想警告您,以防您计划在理解方面做更多工作。