Dafny递归断言违规

时间:2017-06-02 13:56:06

标签: verification dafny

我是dafny的新手,我正试图让这段简单的代码工作。我想计算字符串中char的出现次数。我在第4行收到一个断言违规。我知道我的功能是找到适量的字符,但显然这个断言有一些漏洞。在我开始使用前置条件和后置条件之前,我正试图弄清楚基础知识,如果没有它们,这应该是可能的。该函数只是检查字符串中的最后一个字符并返回1或0,同时再次调用该函数,这会切断字符串的尾部,直到它为空。

method Main() {
  var s:string := "hello world";
  print tally(s, 'l');
  assert tally(s,'l') == 3;
}
function method tally(s: string, letter: char): nat
{
  if |s| == 0 then 0
  else if s[|s|-1] == letter then 1+tally(s[..|s|-1], letter)
  else 0 + tally(s[..|s|-1], letter)
}

http://rise4fun.com/Dafny/2lvt 这是我的代码的链接。

1 个答案:

答案 0 :(得分:1)

认为Dafny静态验证器可以 评估任何代码,例如assert语句中的表达式。该 验证者确实试图评估像这样的表达式 参数以常量形式给出(例如"hello world"'l', 和3)。但是,静态验证程序希望避免递归 永远(甚至递归太长时间),所以它并不总是完全 经历这些表达。在你的情况下,也有限制 验证者能够对序列操作做什么。所以,在 简而言之,尽管验证者试图提供帮助,但并非总是如此 到达递归的底部。

有两种方法可以解决这些限制 验证者接受你的断言。

调试情况最可靠的方法是从较小的开始 输入并构建您正在使用的较长输入。这是相当的 然而,这很乏味,当达夫尼能够做到这一点时,它会让你欣赏 这些东西自动完成以下(验证) 说明你会做什么:

var s := "hello world";
assert tally("he",'l') == 0;
assert tally("hel",'l') == 1;
assert "hell"[..3] == "hel";
assert tally("hell",'l') == 2;
assert "hello"[..4] == "hell";
assert tally("hello",'l') == 2;
assert "hello "[..5] == "hello";
assert tally("hello ",'l') == 2;
assert "hello w"[..6] == "hello ";
assert tally("hello w",'l') == 2;
assert "hello wo"[..7] == "hello w";
assert tally("hello wo",'l') == 2;
assert "hello wor"[..8] == "hello wo";
assert tally("hello wor",'l') == 2;
assert "hello worl"[..9] == "hello wor";
assert tally("hello worl",'l') == 3;
assert s[..10] == "hello worl";
assert tally(s,'l') == 3;

事实上,Dafny验证器没有扩展的东西(太多了 因为你是“采取”行动(即表达的 形式s[..E])。以下中间断言也将 验证自己,并将有助于验证最终的断言。这些 中间断言显示验证者不认为做的事情 自动为你服务。

var s := "hello world";
assert "he"[..1] == "h";
assert "hel"[..2] == "he";
assert "hell"[..3] == "hel";
assert "hello"[..4] == "hell";
assert "hello "[..5] == "hello";
assert "hello w"[..6] == "hello ";
assert "hello wo"[..7] == "hello w";
assert "hello wor"[..8] == "hello wo";
assert "hello worl"[..9] == "hello wor";
assert s[..10] == "hello worl";
assert tally(s,'l') == 3;

你可能想知道,“我怎么能想出这个?”。该 最系统的方式就是在我上面的第一个例子中开始。 然后,你可以尝试修剪那里的许多断言,看看它是什么 是验证者的需要。

(我现在想也许Dafny验证器可能会增强到 也做这些操作。它可能会导致性能问题 别处。我来看看。)

解决验证者限制的另一种方法是定义 函数tally以不同的方式,特别是避免“取” 操作,验证者不想扩展很多。它是 不明白要改变什么以使验证者在这些方面感到高兴 情况,或者如果甚至可能,那么这种解决方法可能会 不是最好的。不过,我尝试了以下定义 tally它会让你的断言通过:

function method tally'(s: string, letter: char): nat
{
  tally_from(s, letter, 0)
}
function method tally_from(s: string, letter: char, start: nat): nat
  requires start <= |s|
  decreases |s| - start
{
  if start == |s| then 0
  else (if s[start] == letter then 1 else 0) + tally_from(s, letter, start+1)
}

请注意,这些定义不使用任何“take”操作。这里, 验证者很乐意将所有递归调用扩展到 找到了最终答案。

Rustan

相关问题