CHAPTER 2 Formalizing Program Syntax
2.1 具体语法 Concrete Syntax
程序的定义是从编程语言的定义开始,而编程语言的定义是从它的语法开始,语法涵盖了哪些种类的短语基本上是格式化的。
具体语法决定了哪些字符序列是可以被接受的。
以简单的算术表达式语言为例,下面的字母串是有效的(valid):
3
x
3 + x
y x (3 + x)
以下字符串是无效的(invalid):
1 + + 2
x y z
这不是简单靠对算术的直觉去评判的,这里遵循一种叫作“巴科斯范式(Backus-Naur Form, BNF)”的语法。
巴科斯范式Backus-Naur Form, BNF
以美国人巴科斯(Backus)和丹麦人诺尔(Naur)的名字命名的一种形式化的语法表示方法,用来描述语法的一种形成体系,是一种典型的元语言。
它有一些语法规则:
· 在双引号中的东西代表它本身
· 双引号外的字代表语法
· <必选项> # 尖括号内表示必选项
· [可选项] #方括号内表示可选项
· {0次或多次} #大括号内表示可重复0次至多次的项
· | 代表 or
· ::= 读作定义为
换句话说,我们先假设常数 (Constants)和变量 (Variables)的集合,分别基于自然数 (N)和字符串 (Strings)的集合,然后定义表达式 (Expression),包含了常数、变量、加法和乘法。
![](https://i-blog.csdnimg.cn/blog_migrate/3daa29467080a8687fd18f34c8adbee2.png)
加法和乘法是递归指定的语法。巴科斯范式用递归来表达一个或多个符号。
举个例子,我们想表达“单词是有字母组成的”,这是日常语言;
那用数学的语言说就是,单词 = 一个字母或多个字母”;
用巴科斯范式的语法表达就是,单词::=<字母>|<字母><单词>,这个表达式里就有了递归。
再例如,“一个正整数是由一个或多个数字构成的”,寻常的表达式无法表达任意位数字构成的正整数,但通过递归可以完美地诠释出来:
<number> ::= <digit>|<digit><number>
<digit> ::= 0|1|2|3|4|5|6|7|8|9
归纳定义法 Inductive Definitions
归纳定义法是一个非常重要的工具,它解释了如何从一个较小的集合建立更大的集合。上面语法中的递归性质隐含地运用了归纳定义。用更通用的话来解释归纳定义就是,它提供了一系列定义集合的推理规则 (inference rules) ,在形式上,这个集合被定义为“满足所有规则的最小集合”。
每个规则都有前提 (premises)和结论 (conclusion),我们用四条规则来诠释上面的BNF语法,两者是完全等价的。
用于定义表达式 (Expressions)的一个集合Exp:
![](https://i-blog.csdnimg.cn/blog_migrate/eb7fc812b639c918b4f0dcbb44fb58e6.png)
这个推理规则这样来解读:
如果横线以上的所有事实都是真的,那么横线以下的事实也是真的。可以将这个表达式看作“if P then Q”的逻辑,分子是前提条件P,分母是Q。这条规则隐晦地表达出需要对其中出现的所有的元变量的值都成立(n, e1, e2称为元变量, metavariables)。
刚接触语义学的人往往会对这种定义风格产生消极的情绪,但很快就会发现它是一种非常紧凑的符号,可以用来表达很多概念,也可以把它看作是数学定义的特定领域的编程语言,这么说会比较抽象,但在Coq代码中可以具体体现出来。
2.2 抽象语法 Abstract Syntax
在简短介绍了具体语法之后,本书剩余部分将会专注于抽象语法,不管具体语法了(感觉自己被耍了,算了)。现在把程序看作是抽象语法树 (Abstract Syntax Trees, ASTs),“”在这里不再是具体语法里乘法的意思,而是笛卡尔积 (Kartesian-product)。Exp
Exp 表示每次分别从这两个类型的集合中取一个值组成一对,组成新的集合。
![](https://i-blog.csdnimg.cn/blog_migrate/010e37422c5d6395587fd37006392ace.png)
推理规则符号Inference-rule Notation
下面是一些推理逻辑的符号表达方式:
再回顾一下上一节说到的逻辑规则“如果横线上面的都是事实,那么横线下面的成立”
![](https://i-blog.csdnimg.cn/blog_migrate/d9a04e4cbd3390d74288e1fa2535bf66.png)
抽象语言用来表示递归定义很方便。下面是构造函数中包含子句类型的例子:
我们更倾向用来代替size(e),左右两边是完全等价的。
![](https://i-blog.csdnimg.cn/blog_migrate/0171739fbbf8bad99df83b132d0c172f.png)
我们用表示e的深度 (depth),即在语法树中根root到叶leaf中向下的最长途径path。
![](https://i-blog.csdnimg.cn/blog_migrate/b0d3251411412c23bc18c66b71004fc6.png)
2.3 结构归纳法原理 Structural Induction Principles
当我们归纳式地定义集合S时,我们的归纳原则是“总有一些P使得S的所有元素都成立”。
回顾前面说的推理逻辑:
![](https://i-blog.csdnimg.cn/blog_migrate/d9a04e4cbd3390d74288e1fa2535bf66.png)
为了推导Exp的结构归纳原理,做出了以下两点修改:
在结论中,
的项都用
来代替,假设P对某些项成立;
在前提条件中,
,增加前提
,假设P对某些项成立。这样的假设佳作inductive hypothesis (IH)
得到的的推理逻辑:
![](https://i-blog.csdnimg.cn/blog_migrate/c914b858dced1c646a15a628f21a5b6a.png)
为了得到我们需要证明这四个推理逻辑都是有效的。
定理2.1 深度永远不能超过大小
符号表达式:
Proof by induction on the structure of e.
这里的证明非常简短,书中对次做出的解释是:
这种极简主义的证明常常让新来者感到惊讶和沮丧。我们在这里的立场是, 证明检查是适合机器的活动,而不是适合人的活动,因此我们将 省略在附带的Coq代码中可以找到的血腥细节,用于这个定理和与本章相关的许多其他定理。事实上,即使是在纸上发表的校样,也倾向于使用像上面那样简短的“校样”,依靠读者的经验来“填空”!不出所料,这类论证经常存在逻辑错误,导致人们接受虚假定理。出于这个原因,我们在这里坚持机器检查的证明,使用书中的章节来介绍概念、推理原则以及关键定理和引理的陈述。
2.4 可判定理论 Decidable Theories
怎么判断一个问题是可判定的呢?
对于一些决策问题(Decision Problems), F为公式的集合,是正确的公式,那么一个决定性问题是可判定的,iff(当且仅当)
(存在)一些始终会停止的程序 s.t.(只有当
时,输入
会返回“true” )
用人话说就是,对于一个决策问题f,如果存在一个程序,当输入f时程序会终止,那么这个f就是可判定的,T就是决策问题集合F中的可判定问题的集合。
一个常见的可判定理论是线性代数,它的决策问题集合F由下面的语法得到,标记为:
![](https://i-blog.csdnimg.cn/blog_migrate/2d7f59626cc90dc180675a7b56075a07.png)
在Coq这样的证明助手中使用dicidable theory,理解一个理论如何适用于实际上不符合其语法的公式是很重要的。例如,我们想证明f(x) - f(x) = 0, 而f(x)是个远超出上述语法的复杂的函数,我们只需要引入新的变量y,使得y = f(x),就有了新的目标 y - y = 0。一个线性代数程序可以在短时间内完成这个目标,然后我们可以重新替换y得出原来的目标。
接下来就是一些关于加法/乘法的交换律/结合律公理。
这一节结尾的例子提到了半环理论,不知道是哪一门数学课没修,脑袋里完全没有这个概念。。。
2.5 简化和重写 Simplification and Rewriting
Expression Size定义
用这个定义来证明公式:
证明:
将定义代入等式左边得出:![]()
因常数的大小为1,所以得出:![]()
由此公式得到证明。
通过证明我们得到了公式:
这种证明的方式就是重写,使用已经得到证明的等式,将想证明的等式左边的子项进行重写替换,使其能够匹配等式右边的子项。