如何在Dafny中编写一个干净的函数来获得最小集合?

时间:2018-05-06 03:02:26

标签: dafny

我正在尝试编写一个函数来获得非空集的最小值。

以下是我提出的建议:

method minimum(s: set<int>) returns (out: int)
requires |s| >= 1
ensures forall t : int :: t in s ==> out <= t
{
  var y :| y in s;
  if (|s| > 1) {
    var m := minimum(s - {y});
    out := (if y < m then y else m);
    assert forall t : int :: t in (s - {y}) ==> out <= t;
    assert out <= y;
  } else {
    assert |s| == 1;
    assert y in s;
    assert |s - {y}| == 0;
    assert s - {y} == {};
    assert s == {y};
    return y;
  }
}

这是次优的,原因有两个:

  • Dafny给出了一个&#34;没有找到触发的条款。&#34;警告线,

    assert forall t : int :: t in (s - {y}) ==> out <= t;
    

    但是,删除此行会导致代码无法验证。我的理解是,触发警告并不是非常糟糕,这只是Dafny可能遇到线路故障的警告。 (尽管它实际上似乎有所帮助。)因此,我觉得我做的事情不太理想或非惯用。

  • 这是非常低效的。 (它每次构造一个新集合,所以它将是O(n ^ 2)。)但是我没有看到任何其他方法来迭代集合。有更快的方法吗?套装是否真正用于编程&#34;真实&#34; Dafny中的非幽灵代码?

所以我的问题(除上述内容外)是:有更好的方法来编写minimum函数吗?

1 个答案:

答案 0 :(得分:1)

在这种情况下,我建议忽略触发警告,因为尽管有警告,它似乎仍能正常工作。 (当涉及到集合理论运算符时,Dafny的触发推断有点过于保守,而Z3能够在低级别推断出良好的触发。)如果你真的想要修复它,这是一种方法。用

替换代码的“then”分支
var s' := (s - {y});
var m := minimum(s');
out := (if y < m then y else m);
assert forall t :: t in s ==> t == y || t in s';
assert forall t : int :: t in s' ==> out <= t;
assert out <= y;

第二个问题(关于效率)有些根本。 (参见Rustan的论文"Compiling Hilbert's Epsilon Operator",其中提到编译let-such-that语句会导致二次性能。)我更倾向于将Dafny的set视为不应编译的数学结构。 (可以编译的事实是玩具程序的便利,而不是真正的系统,人们期望基于数据结构的集合的标准库实现。)