Datalog最早作为数据库的查询语言出现,是非图灵完备的编程语言
语法
和数理逻辑的命题逻辑、谓词逻辑基本是一致的,如果看的教材和我一样是Mathematical Logic in Computer Science的话甚至会发现很多描述的语句都是一样的
,
表示and
;
表示or
!
表示not,这些运算可以用括号改变优先级
每条语句的最后要加.
表示结束,这个老是忘....
Predicate
即谓词。谓词可以看成是一个函数,n元谓词就是一个\(D^n\mapsto \left\{\;0,1\;\right\}\)的函数,其中\(D\)是命题变元(变量)与命题常元(常量)构成的集合
在Datalog中,我们讨论的都是有限论域,因此使谓词P为真的命题有限。记\(F(P)\)为所有在谓词P下为真的n元组,则我们称这些元素为Facts。这样就把一个函数变成判断元素是否属于某集合的问题了。
谓词分为两类,算术谓词x >= y
和关系谓词Friends(x, y)
,算术谓词是Unbounded的
Inference Rules
即推理规则。用PPT的例子就是Datalog的推理规则形如
<- Person(x, age), age >= 18. Adult(x)
<-
可以看成是向左的箭头(好形象!),箭头左侧的称为Head,右侧是Body。语句的意思是"若Body为真,则Head为真"
这句话的意思是"若x是age岁的人 且 age>=18 那么x是成年人"
考虑形式化命题会怎么说
$((x)(Person(x)Age(x)>=18))Adult(x) $
观察区别即可发现,如果不使用函数的概念(形式化命题里的\(Age()\)),我们就需要维护某个元素所有相关的信息,即用元组
求解
就是列真值表,\(\prod\limits_{B\in Body} |B|\)枚举所有可能的值求解\(F(P)\)
Safe Rules
既然涉及到枚举,就要看看是不是可穷尽的
考虑如下两组Rules
<- B(y), x >= y. A(x)
<- B(y), !C(x, y). A(x)
对于1. 如果我们讨论的是正整数域,且\(F(B)\)非空,则必然存在无穷多的x满足要求...
对于2. 由于\(F(C)\)有限,因此\(F(!C)\)有无穷多元素,因此也存在无穷多的x满足要求...
基于上述情况,Datalog对Rules作出了规定:
Head中的变元必须在Body中的某个非否定谓词中出现过(即必须是Bounded Variable)
同样,再考虑如下Rule
<- !A(x). A(x)
很显然就不是合理的Rule,毕竟都不是重言式...因此设定Rules的时候也要考虑是否永真的问题(不能有矛盾)
IDB & EDB
IDB = Intensional DataBase 内涵谓词
EDB = Extensional DataBase 外延谓词
大概理解就是在初始阶段会规定一些谓词,这些就是EDB
而在推理过程中出现的谓词则是IDB。Head一定是IDB,而Body可以IDB、EDB
通常EDB是不可变的(常元),而IDB可以随着程序的进行而修改,例如如果我想表示"喜欢猫 或 喜欢狗 的人是好人",那么我可以写
<- Likes(x, "Dog").
Good(x)
<- Likes(x, "Cat"). Good(x)
当然也可以写
<- Likes(x, "Dog"); Likes(x, "Cat"). Good(x)
递归
Datalog的强大之处就在于递归
考虑给定等价关系集合的传递闭包,就可以这么写
<- Rel(x, y)
Eq(x, y)
<- Eq(x, z), Rel(z, y) Eq(x, y)
其中Rel(x, y)
是EDB中提前定义好的常元
在指针分析中的应用
应用Datalog就能很容易翻译和实现前面讲到的Flow Insensitive & Context Insensitive分析
首先规定下面几个谓词
New(x, o)
Assign(x, y)
Store(x, f, y)
Load(x, y, f)
#l: x.k(a1,a2...an)
VCall(l, x, k)
Dispatch(o, k, m)
ThisVar(m, this)
#ith argument at l: is ai
Argument(l, i, ai)
#ith parameter at method m is pi
Parameter(m, i, pi)
#return var at method m is ret
MethodReturn(m, ret)
#r receives return value at l: CallReturn(l, r)
推导规则就是翻译PPT
<- New(x, o).
VarPointsTo(x, o)
<- VarPointsTo(y, o), Assign(x, y).
VarPointsTo(x, o)
<- VarPointsTo(x, o1), VarPointsTo(y, o2), Store(x,
FieldPointsTo(o1, f, o2)
f, y).
<- VarPointsTo(y, o1), FieldPointsTo(o1, f, o2), Load(x, y,
VarPointsTo(x, o2)
f).
<- VCall(l, x, k),
CallGraph(l, m), Reachable(m), VarPointsTo(this, o)
VarPointsTo(x, o), Dispatch(o, k, m), ThisVar(m, this).
<- CallGraph(l, m), Argument(l, i, ai), Parameter(m, i,
VarPointsTo(pi, o)
pi), VarPointsTo(ai, o).
<- MethodReturn(m, ret), CallGraph(l, m), CallReturn(l, r),
VarPointsTo(r, o) VarPointsTo(ret, o).
全程序分析还要加上一个默认的入口方法,没了