Dafny Dafny
首页
  • 入门介绍

    • 什么是dafny?
  • 用起来吧!

    • 安装
    • 快速上手
    • 可能遇到的问题?
  • Dafny快速入门

    • 基础学习 Basic
    • 方法 Method
    • 关键字 Keyword
    • 函数 Function
    • 类 Class
    • 泛型 Generics
    • 声明 Statement
    • 表达式 Expression
  • Dafny简单例子

    • 寻找最大最小数和
    • 斐波那契数列
    • 线性查询
    • 引理-计算序列非负元素个数
    • 集合
    • 终止
  • Dafny指导

    • 介绍
    • 方法 Methods
    • 前置/后置条件 Pre/Postconditions
    • 断言 Assertions
    • 函数 Functions
    • 循环不变体 Loop Invariants
    • 数组 Arrays
    • 量词(函数) Quantifiers
    • 谓词(函数) Predicates
    • 框架 Framing
    • 二分搜索 Binary Search
    • 总结
  • Dafny进阶语法

    • 引理和归纳 Lemmas and Induction
    • 模块 Modules
    • 集合 sets
    • 序列 sequence
    • 终止 Terminal
    • 值类型 Values Types
  • 实践探索

    • 自动归纳
    • 自动调用引理
    • 定义、证明、算法正确性
    • 各种推导式
    • 不同类型的证明
    • 集合元素上的函数
    • 在集合上的迭代
  • 常用工具

    • Type System
    • Style Guide
    • Cheet Sheet
✨收藏
  • 简体中文
  • English
💬社区留言板
GitHub (opens new window)

Dafny

新一代验证语言
首页
  • 入门介绍

    • 什么是dafny?
  • 用起来吧!

    • 安装
    • 快速上手
    • 可能遇到的问题?
  • Dafny快速入门

    • 基础学习 Basic
    • 方法 Method
    • 关键字 Keyword
    • 函数 Function
    • 类 Class
    • 泛型 Generics
    • 声明 Statement
    • 表达式 Expression
  • Dafny简单例子

    • 寻找最大最小数和
    • 斐波那契数列
    • 线性查询
    • 引理-计算序列非负元素个数
    • 集合
    • 终止
  • Dafny指导

    • 介绍
    • 方法 Methods
    • 前置/后置条件 Pre/Postconditions
    • 断言 Assertions
    • 函数 Functions
    • 循环不变体 Loop Invariants
    • 数组 Arrays
    • 量词(函数) Quantifiers
    • 谓词(函数) Predicates
    • 框架 Framing
    • 二分搜索 Binary Search
    • 总结
  • Dafny进阶语法

    • 引理和归纳 Lemmas and Induction
    • 模块 Modules
    • 集合 sets
    • 序列 sequence
    • 终止 Terminal
    • 值类型 Values Types
  • 实践探索

    • 自动归纳
    • 自动调用引理
    • 定义、证明、算法正确性
    • 各种推导式
    • 不同类型的证明
    • 集合元素上的函数
    • 在集合上的迭代
  • 常用工具

    • Type System
    • Style Guide
    • Cheet Sheet
✨收藏
  • 简体中文
  • English
💬社区留言板
GitHub (opens new window)
  • Dafny教程

  • Dafny进阶语法

    • 引理和归纳
    • 模块
    • 集合
    • 序列
    • 终止
    • 值类型
  • 教程
  • Dafny进阶语法
lijiahai
2022-03-26

序列

# 序列

序列是一种表示有序列表的内置Dafny类型。它们可以用来表示许多有序集合,包括列表、队列、堆栈等。序列是一种不可变的值类型:它们一旦创建就不能被修改。从这个意义上说,它们类似于Java和Python等语言中的字符串,只是它们可以是任意类型的序列,而不仅仅是字符。序列类型如下:

   seq<int>
1

例如,对于一个整数序列。例如,这个函数将一个序列作为参数:

predicate sorted(s: seq<int>)
{
   forall i,j :: 0 <= i < j < |s| ==> s[i] <= s[j]
}
1
2
3
4

序列的长度写成|s|,如上量词所示。使用与数组相同的方括号语法访问序列的特定元素。还请注意,该函数不需要读取子句来访问序列。这是因为序列不是存储在堆上的;它们是值,因此函数在访问它们时不需要声明。序列最强大的属性是注释和函数可以创建和操作序列。例如,另一种表示排序性的方法是递归:如果第一个元素比其他元素小,而其余元素都已排序,则整个数组都已排序:

predicate sorted2(s: seq<int>)
{
   0 < |s| ==> (forall i :: 0 < i < |s| ==> s[0] <= s[i]) &&
               sorted2(s[1..])
}
1
2
3
4
5

年代的符号(1 . .是将序列切片。它的意思是从第一个元素开始,取元素直到最后。这不会修改s,因为序列是不可变的。相反,它创建了一个新的序列,除了第一个元素之外,所有元素都按相同的顺序排列。这类似于整数的加法,因为原始值没有改变,只是创建了新的值。切片表示法是:

   s[i..j]
1

在0 <= i <= j <= |s|这个范围中Dafny将强制执行这些索引边界。结果序列将恰好有j-i元素,并且将从元素s[i]开始,如果结果非空,则将连续地遍历序列。这意味着索引j处的元素被排除在切片之外,这反映了用于常规索引的相同的半开区间。

序列也可以使用显示表示法从其元素构造:

 var s := [1, 2, 3];
1

在这里,我们在一些命令式代码中有一个整数序列变量,其中包含元素1、2和3。这里使用了类型推断来获得序列是整数之一的事实。这种表示法允许我们构造空序列和单例序列:

[] // the empty sequence, which can be a sequence of any type
   [true] // a singleton sequence of type seq<bool>
1
2

切片表示法和显示表示法可以用来检查序列的属性:

method m()
{
   var s := [1, 2, 3, 4, 5];
   assert s[|s|-1] == 5; //access the last element
   assert s[|s|-1..|s|] == [5]; //slice just the last element, as a singleton
   assert s[1..] == [2, 3, 4, 5]; // everything but the first
   assert s[..|s|-1] == [1, 2, 3, 4]; // everything but the last
   assert s == s[0..] == s[..|s|] == s[0..|s|]; // the whole sequence
}
   var s := [1, 2, 3, 4, 5];
   assert s[|s|-1] == 5; //access the last element
   assert s[|s|-1..|s|] == [5]; //slice just the last element, as a singleton
   assert s[1..] == [2, 3, 4, 5]; // everything but the first
   assert s[..|s|-1] == [1, 2, 3, 4]; // everything but the last
   assert s == s[0..] == s[..|s|] == s[0..|s|] == s[..]; // the whole sequence
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

到目前为止,对序列最常见的操作是获取第一个和最后一个元素,以及获取除第一个和最后一个元素以外的所有元素,因为这些通常用于递归函数,如上面的sorted2。除了通过访问或切片来解构序列外,序列还可以使用加号(+)进行连接: 但是Z3定理证明者不会意识到这一点,除非它被一个声明事实的断言提示(关于为什么这是必要的更多信息,请参阅引理/归纳法)。 序列还支持in和!in操作符,用于测试序列中的包含情况:

method m()
{
   var s := [1, 2, 3, 4, 5];
   assert 5 in s;
   assert 0 !in s;
}
   assert 5 in s; // using s from before
   assert 0 !in s;
1
2
3
4
5
6
7
8

这还允许我们在不关心索引的情况下,对序列中的元素进行量化。例如,我们可以要求一个序列只包含该序列的下标元素:

method m()
{
   var p := [2,3,1,0];
   assert forall i :: i in p ==> 0 <= i < |s|;
}
   var p := [2,3,1,0];
   assert forall i :: i in p ==> 0 <= i < |s|;
1
2
3
4
5
6
7

这是序列中每个单独元素的属性。如果我们想让多个元素相互关联,就需要量化指标,如第一个例子所示。

有时我们想使用序列来模拟数组的可更新特性。虽然我们不能改变原来的序列,但我们可以创建一个新序列,其中除了更新的元素外,所有元素都是相同的:

method m()
{
   var s := [1,2,3,4];
   assert s[2 := 6] == [1,2,6,4];
}
   s[i := v] // replace index i by v in seq s
1
2
3
4
5
6

当然,下标i必须是数组的下标。这个语法只是可以通过常规切片和访问操作完成的操作的快捷方式。你能填写下面的代码吗?

function update(s: seq<int>, i: int, v: int): seq<int>
   requires 0 <= index < |s|
   ensures update(s, i, v) == s[i := v]
{
   s[..i] + [v] + s[i+1..]
   // This works by concatenating everything that doesn't
   // change with the singleton of the new value.
}
function update(s: seq<int>, i: int, v: int): seq<int>
   requires 0 <= index < |s|
   ensures update(s, i, v) == s[i := v]
{
   // open in the editor to see the answer.
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

你也可以用数组的元素组成一个序列。这是使用与上面相同的“slice”表示法完成的:

method m()
{
   var a := new int[3]; // 3 element array of ints
   a[0], a[1], a[2] := 0, 3, -1;
   var s := a[..];
   assert s == [0, 3, -1];
}
 var a := new int[3]; // 3 element array of ints
   a[0], a[1], a[2] := 0, 3, -1;
   var s := a[..];
   assert s == [0, 3, -1];
1
2
3
4
5
6
7
8
9
10
11

为了获得数组的一部分,可以像常规切片操作一样给出边界:

method m()
{
   var a := new int[3]; // 3 element array of ints
   a[0], a[1], a[2] := 0, 3, -1;
   assert a[1..] == [3, -1];
   assert a[..1] == [0];
   assert a[1..2] == [3];
}
   assert a[1..] == [3, -1];
   assert a[..1] == [0];
   assert a[1..2] == [3];
1
2
3
4
5
6
7
8
9
10
11

因为序列支持in和!in,这个操作为我们提供了一种简单的方法来表示“element not in array”属性,即将这个转变为

forall k :: 0 <= k < a.Length ==> elem != a[k]
1

这个:

elem !in a[..]
1

此外,边界很容易包括:

forall k :: 0 <= k < i ==> elem != a[k]
1

和以下这个是一样的:

elem !in a[..i]
1
编辑 (opens new window)
上次更新: 2022/03/26, 19:35:15
集合
终止

← 集合 终止→

最近更新
01
寻找最大和最小数
04-06
02
斐波那契数列
04-06
03
线性查询
04-06
更多文章>
Theme by Vdoing | Copyright © 2022-2022 Li Jiahai | Dafny Community | 2022
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式