徐逸辰
Oct, 2020
prop_reverse :: Property
prop_reverse =
property $ do
xs <- forAll $ Gen.list (Range.linear 0 100) Gen.alpha
reverse (reverse xs) === xs
本质上是一种“自动化”的单元测试
gdb
, lldb
coredump
:内存空间快照在编译时发现错误
最简单直接的原因:许多语言的编译器需要通过类型来进行内存分配。
类型 | 大小 |
---|---|
int |
4字节 |
long long |
8字节 |
在编译期明确地指出错误!
def train(model: Model, x: Matrix, edge_index: Matrix, y: Matrix): Double =
// do things meaningful
train(model, data.x, data.edge_index, data.y)
def train(model: Model, x: Matrix, edge_index: Matrix, y: Matrix, early_stopper: EarlyStopper[Double]): Double =
// do things meaningful
train(model, data.x, data.edge_index, data.y) // Compiler Error
在程序运行之前发现并定位错误。
def similarity[N](x: Matrix[N], y: Matrix[N]): Scalar =
x.top dot y
val x: Matrix[N] = ???
val y: Matrix[N] = ???
similarity(x.top, y) // Compiler Error
// Matrix[N] expected, but found Matrix[1, N]
在编译期发现程序中的错误:矩阵维数不匹配。
单元测试:测试没有通过,程序因某种原因出了错。
异常、coredump
:程序在某种实际环境下崩溃了,给出出错原因的线索或排查资料。
类型系统:包含类型错误的程序根本无法编译,给出具体原因:何处的何种类型不匹配,给出期望类型与实际类型。
本质上是通过一些既定的规则,对程序本身进行推理 (reasoning)。
\(Program = Data + Logic\)
\(Data\): 程序当前的状态
一种建模的方法:\(f: String \rightarrow Value\)
赋值语句
\(\forall st \in State\)
程序在st
状态下执行了语句X := expr
状态变换为st'
则可以知道st' = (X -> eval st expr) + st
上面的命题可以写为
forall st, st =[ X := expr ]=> (X -> eval st expr) + st
{ "X" -> 1 } =[ Y := X + 1 ]=> { "Y" -> 2; "X" -> 1 }
对于if
语句:
IF cond DO
stmt1
ELSE
stmt2
END
对于\(\forall st \in State\)
若已知 eval st cond = True
且st =[ stmt1 ]=> st'
则有 st =[ IF ... END ]=> st'
若已知 eval st cond = False
且st =[ stmt2 ]=> st'
则有 st =[ IF ... END ]=> st'
对于while
语句:
WHILE cond DO
stmt
END
对于\(\forall st \in State\)
若已知 eval st cond = False
则直接有 st =[ WHILE ... END ]=> st
若已知 eval st cond = True
且 st =[ stmt ]=> st'
且 st' =[ WHILE ... END ]=> st''
则 st =[ WHILE ... END ]=> st''
WHILE X > 0 DO
X := X - 1;
Y := Y + 1;
END
求证:\(\forall x, y\)
若开始时的状态为 { "X" -> x, "Y" -> y }
在则在执行完程序之后
状态变为 { "X" -> 0, "Y" -> x + y }
\(\forall st \in State\), st =[ X := expr ]=> ( X -> eval st expr ) + st
\(\forall st, st' \in State\), \(\forall P: State \rightarrow Bool\),
st =[ X := expr ]=> st'
若有\(P(st[X \rightarrow\)eval st expr
\(])\)
则\(P(st')\)
记为 { P[X -> expr] } X := expr { P }
谓词 \(P: State \rightarrow Bool\) \(\Rightarrow\) 描述状态的性质
具体的某一个状态的具体变化 \(\Rightarrow\) 状态的性质的变化
要证明赋值语句之后的状态符合某一性质
只需要证明初始状态在根据赋值语句进行名字替换之后符合这一性质
我们得到了霍尔逻辑
上面的三元组被称为霍尔三元组
\[ \frac {} {\{ P \} skip \{ P \}} \]
\[ \frac {} {\{ P[E/x] \} x := E \{ P \}} \]
\[ \frac {\{P\} S \{Q\} \wedge \{Q\} T \{R\}} {\{ P \} S; T \{ R \}} \]
\[ \frac {\{ B \wedge P\} S \{Q\} \wedge \{ \neg B \wedge P\} T \{Q\}} {\{ P \} if \ B \ then \ S \ else \ T \ end \{ Q \}} \]
\[ \frac {\{B \wedge P\} S \{P\}} {\{ P \} while \ B \ do \ S \ end \{ \neg B \wedge P \}} \]
循环不变量!(loop invariant)
WHILE X > 0 DO
X := X - 1;
Y := Y + 1;
END
求证:\(\forall x, y\)
若开始时的状态为 { "X" -> x, "Y" -> y }
在则在执行完程序之后
状态变为 { "X" -> 0, "Y" -> x + y }
{{ X = x /\ Y = y }} ->
{{ X + Y = x + y }} // <~~ The Loop Invariant!
WHILE X > 0 DO
{{ X > 0 /\ X + Y = x + y }} ->
{{ (X - 1) + Y = x - 1 + Y }}
X := X - 1
{{ X + Y = x - 1 + y }} ->
{{ X + (Y + 1) = x - 1 + y + 1 }}
Y := Y + 1
{{ X + Y = x - 1 + y + 1 }} ->
{{ X + Y = x + y }}
END
{{ !(X > 0) /\ X + Y = x + y }} -> // <~~ X >= 0
{{ X = 0 /\ Y = x + y }}
在程序运行之前,从理论上证明程序正确
形式化逻辑:对于逻辑命题的形式化定义与推理
主要工具:递推(归纳)
逻辑验证工具:Coq
实际代码
形式化验证工具中的数据结构
prog =
WHILE X <= 2 DO
X := X + 1;
END
\(\forall n \in \mathbb N_*, n \le 3\)
{ X = n }
prog
{ X = 3 }
日常编程:给予我们思考代码执行过程,进行推理的思维方式(循环不变量)
工业应用:验证高成本、高要求软件的正确性
Software Foundations (Volume 1 and 2)
Types and Programming Languages
欢迎加入字母表俱乐部!
QQ群 749999314