Software Foundations Vol.I : 使用结构化的数据(Lists)

news/2025/10/6 1:37:12/文章来源:https://www.cnblogs.com/mesonoxian/p/19127210

Software Foundations Vol.I : 使用结构化的数据(Lists)


数值序对

在 Inductive 类型定义中,每个构造子(Constructor)可以有任意多个参数 —— 可以没有(如 true 和 O),可以只有一个(如 S),也可以更多 (如 nybble,以及下文所示):

Inductive natprod : Type :=
| pair (n1 n2 : nat).Check (pair 3 5) : natprod.Definition fst (p : natprod) : nat :=match p with| pair x y ⇒ xend.
Definition snd (p : natprod) : nat :=match p with| pair x y ⇒ yend.
Compute (fst (pair 3 5)).
(* ===> 3 *)

由于二元组十分常用,不妨以标准的数学记法 (x,y) 取代 pair x y。 通过 Notation 向 Coq 声明该记法:

Notation "( x , y )" := (pair x y).

这种新的表示法既可以在表达式中使用,也可以在模式匹配中使用.

Compute (fst (3,5)).
Definition fst' (p : natprod) : nat :=match p with| (x,y) ⇒ xend.
Definition snd' (p : natprod) : nat :=match p with| (x,y) ⇒ yend.
Definition swap_pair (p : natprod) : natprod :=match p with| (x,y) ⇒ (y,x)end.

对一个括号内的元组(如:(x,y)),并非与先前所见的多重匹配语法(如 x,y)混淆.

这种差异虽然不大,但是了解到这一点还是非常必要的.例如,下面的定义就是不正确的:

(* Can't match on a pair with multiple patterns: *)
Definition bad_fst (p : natprod) : nat :=match p with| x, y ⇒ xend.(* Can't match on multiple values with pair patterns: *)
Definition bad_minus (n m : nat) : nat :=match n, m with| (O , _ ) ⇒ O| (S _ , O ) ⇒ n| (S n', S m') ⇒ bad_minus n' m'end.

如果我们以稍显古怪的方式陈述序对的性质,那么有时只需 reflexivity(及其内建的简化)即可完成证明。

Theorem surjective_pairing' : ∀ (n m : nat),(n,m) = (fst (n,m), snd (n,m)).
Proof.reflexivity. Qed.

但是,如果我们用一种更为自然的方式来陈述此引理的话,只用 reflexivity 还不够。

Theorem surjective_pairing_stuck : ∀ (p : natprod),p = (fst p, snd p).
Proof.simpl. (* 啥也没有归约! *)
Abort.

我们还需要向 Coq 展示 p 的具体结构,这样 simpl 才能对 fst 和 snd 做模式匹配。通过 destruct 可以达到这个目的

Theorem surjective_pairing : forall (p : natprod),p = (fst p, snd p).
Proof.intros p. destruct p as [n m]. - simpl. reflexivity. 
Qed.

数值列表

通过推广序对的定义,数值'列表'类型可以这样描述: “一个列表要么是空的,要么就是由一个数和另一个列表组成的序对。”

Inductive natlist : Type :=| nil| cons (n : nat) (l : natlist).

例如,这是一个三元素列表:

Definition mylist := cons 1 (cons 2 (cons 3 nil)).

和序对一样,使用熟悉的编程记法来表示列表会更方便些。 以下两个声明能让我们用 :: 作为中缀的 cons 操作符, 用方括号作为构造列表的“外围(outfix)”记法。

Notation "x :: l" := (cons x l)(at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).

注解 "right associativity" 告诉 Coq 当遇到多个 :: 时如何给表达式加括号,如此一来下面三个声明做的就是同一件事:

Definition mylist1 := 1 :: (2 :: (3 :: nil)).
Definition mylist2 := 1 :: 2 :: 3 :: nil.
Definition mylist3 := [1;2;3].

"at level 60" 告诉 Coq 当遇到表达式和其它中缀运算符时应该如何加括号。 例如,plus 函数定义了 + 中缀符号,它的优先级是 50:

Notation "x + y" := (plus x y)(at level 50, left associativity).

+会比 :: 结合的更紧密,所以 1 + 2 :: [3] 会被解析成 (1 + 2) :: [3] 而非 1 + (2 :: [3])。

Repeat

接下来看几个用来构造和操作列表的函数。第一个是 repeat 函数,它接受一个数字 n 和一个 count,返回一个长度为 count,每个元素都是 n 的列表。

Fixpoint repeat (n count : nat) : natlist :=match count with| O ⇒ nil| S count' ⇒ n :: (repeat n count')end.

Length

length 函数用来计算列表的长度。

Fixpoint length (l:natlist) : nat :=match l with| nil ⇒ O| h :: t ⇒ S (length t)end.

Append

app 函数用来把两个列表联接起来。

Fixpoint app (l1 l2 : natlist) : natlist :=match l1 with| nil ⇒ l2| h :: t ⇒ h :: (app t l2)end.

由于下文中 app 随处可见,不妨将其定义为中缀运算符。

Notation "x ++ y" := (app x y)(right associativity, at level 60).
Example test_app1: [1;2;3] ++ [4;5] = [1;2;3;4;5].
Proof. reflexivity. Qed.
Example test_app2: nil ++ [4;5] = [4;5].
Proof. reflexivity. Qed.
Example test_app3: [1;2;3] ++ nil = [1;2;3].
Proof. reflexivity. Qed.

Head与Tail

下面介绍列表上的两种运算:hd 函数返回列表的第一个元素(即“head”); tl 函数返回列表除去第一个元素以外的部分(即“tail”)。由于空表没有表头, 我们必须传入一个参数作为返回的默认值。

Definition hd (default:nat) (l:natlist) : nat :=match l with| nil ⇒ default| h :: t ⇒ hend.
Definition tl (l:natlist) : natlist :=match l with| nil ⇒ nil| h :: t ⇒ tend.
Example test_hd1: hd 0 [1;2;3] = 1.
Proof. reflexivity. Qed.
Example test_hd2: hd 0 [] = 0.
Proof. reflexivity. Qed.
Example test_tl: tl [1;2;3] = [2;3].
Proof. reflexivity. Qed.

用列表实现口袋(Bag)

bag(或者叫 multiset 多重集)类似于集合,只是其中每个元素都能出现不止一次。 口袋的一种可行的表示是列表。

Definition bag := natlist.

有关列表的论证

和数字一样,有些列表处理函数的简单事实仅通过化简就能证明。

Theorem nil_app : ∀ l:natlist,[] ++ l = l.
Proof. reflexivity. Qed.

...由于 [] 被替换进了 app 定义中相应的“被检”分支 (即经由匹配“仔细检查”过值的表达式),整个匹配得以被简化。

和数字一样,有时对一个列表做分类讨论(是否是空)是非常有用的。

Theorem tl_length_pred : ∀ l:natlist,pred (length l) = length (tl l).
Proof.intros l. destruct l as [| n l'].- (* l = nil *)reflexivity.- (* l = cons n l' *)reflexivity. Qed.

在这里 nil 的情况能够工作是因为定义了 tl nil = nil, 而 destruct 策略中 as 注解引入的两个名字,n 和 l',分别对应了 cons 构造子的两个参数(正在构造的列表的头和尾)。

然而一般来说,许多关于列表的有趣定理都需要用到归纳法来证明。

对列表进行归纳

比起对自然数的归纳,归纳证明 natlist 这样的数据类型更加陌生。 不过基本思路同样简单。每个 Inductive 声明定义了一组数据值, 这些值可以用声明过的构造子来构造。

归纳定义的集合中元素的形式 '只能是' 构造子对其它项的应用。这一事实同时也给出了一种对归纳定义的集合进行论证的方法:一个自然数要么是 O,要么就是 S 应用到某个'更小'的自然数上;一个列表要么是 nil, 要么就是 cons 应用到某个数字和某个'更小'的列表上,诸如此类。

  • 首先,证明当 l 为 nil 时 P l 成立。

  • 然后,证明当 l 为 cons n l' 时 P l 成立,其中 n 是某个自然数,l' 是某个更小的列表,假设 P l' 成立.

由于较大的列表总是能够分解为较小的列表,最终这个较小的列表会变成 nil,这两点合在一起就完成了 P 对一切列表 l 成立的证明。下面是个具体的例子:

Theorem app_assoc : forall l1 l2 l3 : natlist,(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.intros l1 l2 l3. induction l1 as [| n l1' IHl1'].- (* l1 = nil *)reflexivity.- (* l1 = cons n l1' *)simpl. rewrite -> IHl1'. reflexivity. 
Qed.

其使用自然语言描述为:

'定理':对所有的列表 l1, l2, 和 l3, (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3)。
'证明': 通过对 l1 使用归纳法。

首先, 假设 l1 = []。我们必须证明:
$$([] ++ l2) ++ l3 = [] ++ (l2 ++ l3)$$

这可以通过展开 ++ 的定义得到。

然后, 假设 l1 = n::l1',有:
$$(l1' ++ l2) ++ l3 = l1' ++ (l2 ++ l3)$$

(归纳假设)。我们必须证明:
$$((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ (l2 ++ l3)$$

根据 ++ 的定义, 上式等价于:
$$n :: ((l1' ++ l2) ++ l3) = n :: (l1' ++ (l2 ++ l3))$$

该式可通过我们的归纳假设立即证得。 ☐

反转列表

举一个更加深入的例子来说明对列表的归纳证明:假设使用 app 来定义一个列表反转函数 rev:

Fixpoint rev (l:natlist) : natlist :=match l with| nil ⇒ nil| h :: t ⇒ rev t ++ [h]end.
Example test_rev1: rev [1;2;3] = [3;2;1].
Proof. reflexivity. Qed.
Example test_rev2: rev nil = nil.
Proof. reflexivity. Qed.

为了比目前所见的证明多一点挑战性, 来证明反转一个列表不会改变它的长度。

Theorem rev_length_firsttry : ∀ l : natlist,length (rev l) = length l.
Proof.intros l. induction l as [| n l' IHl'].- (* l = nil *)reflexivity.- (* l = n :: l' *)(* 这种情况比较棘手。我们从一般的化简开始。 *)simpl.(* 现在我们好像卡住了:目标是要证明涉及 ++ 的等式,但是我们在上下文和全局环境下并没有任何有用的等式!通过用 IH 来重写目标,我们可以推进一点... *)rewrite <- IHl'.(* ...但也仅此而已。 *)
Abort.

不妨单独提出引理,阐述 ++ 与 length 形成的等式关系, 以从我们卡住的地方推进证明。

Theorem app_length : ∀ l1 l2 : natlist,length (l1 ++ l2) = (length l1) + (length l2).
Proof.(* 课上已完成 *)intros l1 l2. induction l1 as [| n l1' IHl1'].- (* l1 = nil *)reflexivity.- (* l1 = cons *)simpl. rewrite → IHl1'. reflexivity. 
Qed.

现在可以完成最初的证明了。

Theorem rev_length : ∀ l : natlist,length (rev l) = length l.
Proof.intros l. induction l as [| n l' IHl'].- (* l = nil *)reflexivity.- (* l = cons *)simpl. rewrite → app_length.simpl. rewrite → IHl'. rewrite plus_comm.reflexivity.
Qed.

'定理':对于所有 l,length (rev l) = length l。
'证明':首先,观察到 length (l ++ [n]) = S (length l) 对一切 l 成立, 这一点可通过对 l 的归纳直接得到。
当 l = n'::l' 时,通过再次对 l 使用归纳, 然后同时使用之前观察得到的性质和归纳假设即可证明。 ☐

Search 搜索

我们已经见过很多需要使用之前证明过的结论(例如通过 rewrite)来证明的定理了。 但是在引用别的定理时,我们必须事先知道它们的名字。

当然,即使是已被证明的定理本身 我们都不能全部记住,更不用提它们的名字了。

Coq 的 Search 指令在这时就非常有用了。执行 Search foo 会让 Coq 显示所有涉及到 foo 的定理。

例如,去掉下面的注释后, 会看到一个我们证明过的所有关于 rev 的定理的列表:

(*  Search rev. *)

Options 可选类型

假设我们想要写一个返回某个列表中第 n 个元素的函数。如果我们为它赋予类型 nat → natlist → nat,那么当列表太短时我们仍须返回某个数...

Fixpoint nth_bad (l:natlist) (n:nat) : nat :=match l with| nil ⇒ 42| a :: l' ⇒ match n with| 0 ⇒ a| S n' ⇒ nth_bad l' n'endend.

这种方案并不好:如果 nth_bad 返回了 42,那么不经过进一步处理的话, 无法得知该值是否真的出现在了输入中。

因为无法判断是什么因素让它返回了 42.它可能是列表过短时的返回值,同时也可能是(此时列表足够长)在列表中找到的值

Inductive natoption : Type :=| Some (n : nat)| None.

可以修改前面 nth_bad 的定义,使其在列表太短时返回 None, 在列表足够长且 a 在 n 处时返回 Some a。将这个新函数称为 nth_error 来表明它可以产生带错误的结果。

Fixpoint nth_error (l:natlist) (n:nat) : natoption :=match l with| nil ⇒ None| a :: l' ⇒ match n with| O ⇒ Some a| S n' ⇒ nth_error l' n'endend.
Example test_nth_error1 : nth_error [4;5;6;7] 0 = Some 4.
Example test_nth_error2 : nth_error [4;5;6;7] 3 = Some 7.
Example test_nth_error3 : nth_error [4;5;6;7] 9 = None.

Coq 的条件语句和其它语言中的一样,不过加上了一点更为一般化的特性。

由于 bool 类型不是内建的,因此 Coq 实际上支持在'Any'带有两个构造子的, 归纳定义的类型上使用条件表达式。

当断言(guard)求值为 Inductive 定义中的第一个构造子时,它被认为是真的;当它被求值到第二个构造子时, 则被认为是假的。

Fixpoint nth_error' (l:natlist) (n:nat) : natoption :=match l with| nil ⇒ None| a :: l' ⇒ if n =? O then Some aelse nth_error' l' (pred n)end.

以下函数从 natoption 中取出一个 nat,在遇到 None 时它将返回提供的默认值。

Definition option_elim (d : nat) (o : natoption) : nat :=match o with| Some n' ⇒ n'| None ⇒ dend.

Partial Maps 偏映射

下面的例子演示了如何在 Coq 中定义基础的数据结构。这是一个简单的 '偏映射' 数据类型,它类似于大多数编程语言中的映射或字典数据结构。

首先,定义一个新的归纳数据类型 id 来用作偏映射的“键”。

Inductive id : Type :=| Id (n : nat).

本质上来说,id 只是一个数。但通过 Id 标签封装自然数来引入新的类型, 能让定义变得更加可读,同时也给了我们更多的灵活性。

还需要一个 id 的相等关系测试:

Definition eqb_id (x1 x2 : id) :=match x1, x2 with| Id n1, Id n2 ⇒ n1 =? n2end.

现在定义偏映射的类型:

Inductive partial_map : Type :=| empty| record (i : id) (v : nat) (m : partial_map).

此声明可以理解为:“有两种方式可以构造一个 partial_map:
用构造子 empty 表示一个空的偏映射,
或将构造子 record 应用到一个键、一个值和一个既有的 partial_map 来构造一个带“键-值”映射 的 partial_map。”

update 函数用于在部分映射中覆盖给定的键以取缔原值(如该键尚不存在, 则新建其记录)。

Definition update (d : partial_map)(x : id) (value : nat) : partial_map :=record x value d.

最后,find 函数按照给定的键搜索一个 partial_map。若该键无法找到, 它就返回 None;若该键与 val 相关联,则返回 Some val。 若同一个键被映到多个值,find 就会返回它遇到的第一个值。

Fixpoint find (x : id) (d : partial_map) : natoption :=match d with| empty ⇒ None| record y v d' ⇒ if eqb_id x ythen Some velse find x d'end.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/928906.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云1M做网站闸北区网站建设网

狂神说SpringBoot系列连载课程&#xff0c;通俗易懂&#xff0c;基于SpringBoot2.2.5版本&#xff0c;欢迎各位狂粉转发关注学习。未经作者授权&#xff0c;禁止转载分布式理论什么是分布式系统&#xff1f;在《分布式系统原理与范型》一书中有如下定义&#xff1a;“分布式系统…

Software Foundations Vol.I : 归纳证明(Induction)

Software Foundations Vol.I : 归纳证明(Induction)归纳法证明 我们在上一章中通过基于化简的简单论据证明了 0 是 + 的左幺元。 我们也观察到,当我们打算证明 0 也是 + 的 右 幺元时事情就没这么简单了 Theorem plus…

外贸网站建设和seo海淀网站建设多少钱

本质上, 这两种方式都是加载CSS文件, 但还是存在着细微的差别 import 机制不同于link&#xff0c;link是加载页面前css加载完毕&#xff0c;import 是先读取文件再加载import是css2.0里的 ie5以上不支持用js控制dom时改变样式&#xff0c;只能用link&#xff0c;import不是dom…

广州网站建设app开发哈尔滨网页模板建站

首先思考一个问题&#xff1a;如果 Dubbo 应用使用 ZooKeeper 作为注册中心&#xff0c;现在需要切换到新的 ZooKeeper 实例&#xff0c;如何做到流量无损&#xff1f; 本文提供解决这个问题的一种方案。 场景 有两个基于 Dubbo 的微服务应用&#xff0c;一个是服务提供者&…

Software Foundations Vol.I : Coq函数式编程(Basics)

好久没写随笔了,随便投投.这里的内容主要取至《软件基础》第一卷https://coq-zh.github.io/. 我补充了一部分习题的答案,在https://github.com/mesonoxian-yao/softwareFoudations-volume1-coqLearn可以看看. Software…

Python 在自然语言处理中的应用与发展

一、引言 🗣️ 自然语言处理(NLP)是人工智能的重要分支,旨在让计算机理解、生成和处理人类语言。它广泛应用于 搜索引擎、机器翻译、智能客服、情感分析、舆情监测 等场景。Python 作为 AI 研究和工程实现的主流语…

专业的深圳网站建设公司排名深圳网站制作公司在那

文章目录 解决TensorRT加速推理SDXL出现黑图问题1. fp162. 更换vae 解决TensorRT加速推理SDXL出现黑图问题 一般产生黑图&#xff0c;仅需要修改下面两个问题即可解决。 1. fp16 将pipeline中的fp16修改为fp32。 在使用稳定扩散&#xff08;Stable Diffusion&#xff09;生…

影响网站可用性的因素青州网站设计

文章目录 协议局域网通信IP 地址网络通信的本质tcp 和 udp 协议网络字节序网络主机数据转化接口 协议 协议&#xff1a;收到数据后&#xff0c;多出来的那一部分&#xff0c;也叫一种 “约定”&#xff0c;一整套的自硬件到软件&#xff0c;都有协议&#xff0c;需要有人定制&a…

Python 在网络爬虫与数据采集中的应用

一、引言 🌐 在大数据时代,数据已成为企业与科研的核心资产。然而,很多数据并不以结构化形式直接提供,需要通过网络爬虫和采集技术来获取。Python,凭借其简洁的语法、强大的第三方库和活跃的社区支持,已经成为网…

开通网站主机站长统计网站统计

栅格数据是一种从远处表示地球表面和大气的地理空间数据,通常使用卫星或航空传感器。它是一种基于网格的数据结构,其中网格中的每个单元或像素代表地球表面的特定位置。这些像素可以存储各种类型的信息,例如海拔、温度、土地覆盖、降水量或与该位置相关的任何其他连续或分类…

做网站和seo哪个好网站制作的收费标准

虚拟机Linux系统网络配置&#xff1a; 1、Vmware网络设置 虚拟机设置->网路适配器->网络连接 桥接模式&#xff1a;能提供独立的IP地址的情况下使用NAT模式&#xff1a;一台计算机只能使用一个IP&#xff0c;主机与虚拟机共享IP&#xff0c;外部网络无法发现虚拟机&#…

wordpress 企业整站源码岳阳网站建设网站

点击访问体验 之前有体验过github的代码助手&#xff0c;奈何收费了&#xff0c;上周发现有一个免费的代码助手。 下载安装 vscode 搜索扩展 TONGYI Lingma 安装完成后登陆即可体验 写注释让他写代码 根据上下文自动补充 这里我只写了一个方法名&#xff0c;getAgencyList…

15_spring_data_neo4j简单教程

Spring Data Neo4j 简单教程 简介 Spring Data Neo4j 是 Spring Data 项目的一部分,它提供了对 Neo4j 图数据库的集成支持。通过 Spring Data Neo4j,开发者可以轻松地在 Spring Boot 应用中使用 Neo4j 数据库,利用图…

珠海网站制作哪家便宜久久建筑网 百度网盘

认证与权限频率组件 身份验证是将传入请求与一组标识凭据&#xff08;例如请求来自的用户或其签名的令牌&#xff09;相关联的机制。然后 权限 和 限制 组件决定是否拒绝这个请求。 简单来说就是&#xff1a; 认证确定了你是谁权限确定你能不能访问某个接口限制确定你访问某…

成都网站建设制作设计内蒙古最新消息今天

Python是一种高级编程语言&#xff0c;广泛用于数据科学、人工智能、网络编程等领域。 Python提供了许多内置函数和标准库&#xff0c;可以完成各种任务&#xff1a; 1、print()函数&#xff1a;将文本输出到控制台。可以将字符串、数字和变量等输出到控制台。 2、input()函…

创建自己的网站怎么弄宁乡市住房和城乡建设局网站

原理概述 当一台BGP路由器中存在多条去往同一目标网络的BGP路由时&#xff0c;BGP协议会对这些BGP路由的属性进行比较&#xff0c;以确定去往该目标网络的最优BGP路由&#xff0c;然后将该最优BGP路由与去往同一目标网络的其他协议路由进行比较&#xff0c;从而决定是否将该最优…

重庆商家网站农村自建房设计师哪里找

目录 Python基础&#xff08;八&#xff09;--迭代&#xff0c;生成器&#xff0c;装饰器与元类 1 迭代 1.1 可迭代对象与迭代器 1.2 自定义迭代类型 1.3 迭代合体 2 生成器 2.1 什么是生成器 2.2 生成器表达式 2.3 生成器函数 3 装饰器 3.1 闭包 3.2 什么是装饰器 …

如何向百度举报网站国外网站怎么上

2019独角兽企业重金招聘Python工程师标准>>> 安装。。。后查看 import django django.VERSION #输出版本号&#xff0c;目前自己是py2.7.9和django1.8 1&#xff0c;新建一个django-project django-admin.py startproject project-name 一个project一般为一个项目 …

网站制作费用申请移动互联网开发记事本项目告别

这一篇讲解消费者 文章目录一、依赖配置1. 引入依赖2. 配置文件3. 主配置二、代码Conding2.1. 消费者代码一、依赖配置 1. 引入依赖 <!--springboot整合RabbitMQ依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>sp…

济南seo外贸网站建设小型公司网站建设

AV1 屏幕内容编码 为了提高屏幕捕获内容的压缩性能&#xff0c;AV1采用了几种编码工具&#xff0c;例如用于处理屏幕画面中重复模式的内帧内块复制&#xff08;IntraBC&#xff09;&#xff0c;以及用于处理颜色数量有限的屏幕块的调色板模式。 帧内块拷贝 AV1 编码中的 Intra …