prolog 笔记

?- ['/Users/qianruzhou/Documents/code/my/my_prolog/db.pl'].
true.

// 在查询界面直接加规则,需要用下面的方法,control+D 结束输入规则。
?- [user].
|: carnivore(X) :- animal(X), eats_meat(X).
|: <EOF> % user://1 compiled 0.00 sec, 880 bytes
true.

// 或者是用assert
?- assert((carnivore(X) :- animal(X), eats_meat(X))).

asserta(X) 增加一个子句X
retract(X) 删除一个字句X,把子句X从动态数据库中删除。此操作也是永久性的,也就是说回溯的时候不能撤销此操作。
assert(X) 和asserta/1的功能类似,只不过它把X子句追加为最后一个子句。

* 在swi prolog中需要对动态操作的谓词名进行声明,例如前面如果希望能够动态修改people/1的子句,需要在程序最前面运行:
:-dynamic people/1.


Prolog的程序就是谓词的数据库
规则的实质就是储存起来的查询。
规则的运行是通过Prolog内建的回溯功能实现的。
我们可以使用内部谓词fail来强制实现回溯。
我们也可以通过加入一条参数为伪变量(下划线)无Body部分的子句,来实现强制让谓词成功。

数据库中的事实代替了一般语言中的数据结构。
回溯功能能够完成一般语言中的循环操作。
而通过模式匹配能够完成一般语言中的判断操作。
规则能够被单独地调试,它和一般语言中的模块相对应。
而规则之间的调用和一般语言中的函数的调用类似。

Prolog子句中的变量全部都是局部变量。与其他的语言不同,在Prolog中没有全局变量,取而代之的是Prolog的数据库。它使得所有的Prolog子句能够共享信息。而asserts和retracts就是控制这些全局数据的工具。
使用全局数据有助于在子句之间快速的传递信息。不过,这种方式隐藏了子句之间的调用关系,所以一旦程序出错,是很难找到原因的

在Prolog中的not的意思是:不能通过当前数据库中的事实和规则推出查询的目标。

此处的等号在Prolog中的意义与其他语言中的不同。它不是数学运算符或者赋值符。
使用=进行联合操作与Prolog使用目标与子句联合时相同。在回溯时,变量将被释放。

=只进行联合而不进行计算

object(apple, size:small, color:red, weight:1).

?- object(X, size:small, color:C, weight:W).
X = apple,
C = red,
W = 1.

?- [a|[b,c,d]] = [a,b,c,d].
yes

上面的联合之所以成功,是因为等号两边的列表是等价的。注意表尾tail一定是列表,而表头则是一个项目,可以是表,也可以是其他的任何数据结构。下面的匹配失败,在“|”之后只能是一个列表,而不能是多个项目。

?- [a|b,c,d] = [a,b,c,d].
no

空表不能与[H|T]匹配,因为它没有表头。
?- [H|T] = [].
no
注意:最后这个匹配失败非常重要,在递归过程中经常使用它作为边界检测。即只要表不为空,那么它就能与[X|Y]匹配,当表为空时,就不能匹配,表示已经到达的边界条件。

我们还可以在第二个项目后面使用“|”,事实上,|前面的都是项目,后面的是一个表。

?- [One, Two | T] = [apple, sprouts, fridge, milk].
One = apple
Two = sprouts
T = [fridge, milk]

请注意下面的例子中变量是如何与结构绑定的。内部变量现实除了变量之间的联系。
?- [X,Y|T] = [a|Z].
X = a,
Z = [Y|T].

表可以看作是表头项目与表尾列表组合而成。而表尾列表又是由同样的方式组成的。所以表的定义本质上是递归定义。我们来看看下面的例子。
?- [a|[b|[c|[d|[]]]]] = [a,b,c,d].
yes

前面我们说过,列表是一种特殊的结构。最后的这个例子让我们对表的理解加深了。它事实上是一个有两个参数的谓词。第一个参数是表头项目,第二个参数是表尾列表。如果我们把这个谓词叫做dot/2的话,那么列表[a,b,c,d]可以表示为:
dot(a,dot(b,dot(c,dot(d,[]))))
事实上,这个谓词是存在的,至少在概念上是这样,我们用“.”来表示这个谓词,读作dot。
我们可以使用内部谓词display/1来显示dot,它和谓词write/1大致上相同,但是当它的参数为列表时将使用dot语法来显示列表。

?- X=[a,b,[c,d],e], write(X), nl, display(X), nl.
[a,b,[c,d],e]
'[|]'(a,'[|]'(b,'[|]'('[|]'(c,'[|]'(d,[])),'[|]'(e,[]))))
X = [a, b, [c, d], e].

从这个例子中我们可以看出为什么不使用结构的语法来表示列表。因为它太复杂了,不过实际上列表就是一种嵌套式的结构。这一点在我们编制列表的谓词时应该牢牢地记住。

append([],X,X).

append([H|T1],X,[H|T2]) :- append(T1,X,T2).

Prolog真正独特的地方就在这里了。在每一层都将有新的变量被绑定,它们和上一层的变量联合起来。第二条子句的递归部分的第三个参数T2,与其头部的第三个参数的表尾相同,这种关系在每一层中都是使用变量的绑定来体现的。

下面三个定义是同样的功能:
add_thing(NewThing, Container, NewList):-
    loc_list(OldList, Container),
    append([NewThing],OldList, NewList).

add_thing2(NewThing, Container, NewList):-
    loc_list(OldList, Container),
    NewList = [NewThing | OldList].

add_thing3(NewTh, Container,[NewTh|OldList]) :-
    loc_list(OldList, Container).

下面的put_thing/2,能够直接修改动态数据库,请自己研究一下。

put_thing(Thing,Place) :-
    retract(loc_list(List, Place)),
    asserta(loc_list([Thing|List],Place)).

到底是使用多条子句,还是使用列表方式,这完全有你的编程习惯来决定。有时使用Prolog的自动回溯功能较好,而有时则使用递归的方式较好。还有些较为复杂的情况,需要同时使用子句和列表来表达数据。 这就必须掌握两种数据表达方式之间的转换。
把一个列表转换为多条子句并不难。使用递归过程逐步地把表头asserts到数据库中就行了。


?- 4+1 is 3+2.
false. 3+2 is evaluated to 5. 4+1 is not evaluated. So 4+1 is different from

?- 4+1=3+2.
false. Neither side is evaluated by =. The two expressions are different.


?- 4+1 =:= 3+2.
true. Both sides are evaluated by =:=

= 时赋值不计算, is 计算 而=:=是两头都计算

cut,使用符号!来表示。
Cut能够有效地剔除一些多余的搜索。如果在cut处产生回溯,它会自动地失败,而不去进行其它的选择。
cut是不符合纯逻辑学的,不过出于实用的考虑,它还是必须的。过多地使用cut将降低程序的易读性和易维护性。它就像是其它语言中的goto语句。
当你能够确信在谓词中的某一点只有一个答案,或者没有答案时,使用cut可以提高程序的效率,另外,如果在某种情况下你想让某个谓词强制失败,而不让它去寻找更多的答案时,使用cut也是个不错的选择。

事实上not/1子句可以使用cut来定义,它同时还用到了另一个内部谓词call/1。call/1把它的参数作为谓词来调用。
not(X) :- call(X), !, fail.
not(X).

repeat/0。它在第一次调用时永远成功,并且在回溯时也永远成功。换句话说,流程不可能回溯通过repeat/0。
如果某个子句中有repeat/0,并且其后有fail/0谓词出现,那么将永远循环下去。使用这种方法可以编写死循环的Prolog程序

把列表的元素顺序倒过来的谓词也可以使用尾递归来完成。

naive_reverse([],[]).
naive_reverse([H|T],Rev):-
naive_reverse(T,TR),
append(TR,[H],Rev).

?- naive_reverse([ants, mice, zebras], X).
X = [zebras, mice, ants]

这个谓词在逻辑上是完全正确的,不过它的运行效率非常低。所以我们把它叫做原始(naive)的递归。
当引入一个用来储存部分运算结果的新的参数后,我们就可以使用尾递归来重写这个谓词。

reverse([], Rev, Rev).
reverse([H|T], Temp, Rev) :-
reverse(T, [H|Temp], Rev).

我们需要介绍一种新的数据结构:差异表。它由两个相关的表构成,第一个表称为全表,而第二个表称为余表。这两个表可以作为谓词的两个参数,不过我们通常使用‘-’连接这两个表,这样易于阅读。它的形式是X-Y。

你可以把任何谓词定义为操作符的形式,例如,如果我们把location/2定义为了操作符,那么我们就可以用:
apple location kitchen.
来代替
location(apple, kitchen).

操作符有三种形式:
中缀(infix):例如3+4
前缀(prefix):例如-7
后缀(postfix):例如8 factorial

每个操作符有不同的优先权值,从11200。当某句中有多个操作符时,优先权高的将先被考虑。优先权值越小优先权越高。
使用内部谓词op/3来定义操作符,它的三个参数分别是:优先权、结合性、操作符名称。
结合性使用模板来定义,例如中缀操作符使用“xfx”来定义。“f”表示操作符的位置。

当操作符的优先权相同时,Prolog必须决定是从左到右还是从右到左地读入操作符。这就是操作符的左右结合性。有些操作符没有结合性,如果你把两个这种操作符放到一起将产生错误。
下面是结合性的模板:

Infix:
xfx non-associative (没有结合性)
xfy right to left
yfx left to right

Prefix
fx non-associative
fy left to right

Postfix:
xf non-associative
yf right to left

前面所定义的谓词is_in/2没有结合性,所以下面的句子是错误的。
key is_in desk is_in office.
为了表示这种嵌套关系,我们可以使用从右到左的结合性。
?- op(35,xfy,is_in).
yes

?- display(key is_in desk is_in office).
is_in(key, is_in(desk, office))

如果使用从左到右的结合性,我们的结果将不同。
?- op(35,yfx,is_in).
yes
?- display(key is_in desk is_in office).
is_in(is_in(key, desk), office)
但是使用括号可以改变这种结合性:
?- display(key is_in (desk is_in office)).
is_in(key, is_in(desk, office))


在Prolog中经常用到差异表,因此许多Prolog版本都对差异表有很好的支持,这样就可以隐去差异表的一些繁琐复杂之处。这种语法称为Definite Clasue Grammer(DCG),它看上去和一般的Prolog子句非常相似,只不过把连接符:-替换成为–>,这种表达形式由Prolog翻译成为普通的差异表形式。


使用DCG,原来的句子谓词将写为:
sentence --> nounphrase, verbphrase.
这个句子将被翻译成一般的使用差异表的Prolog子句,但是这里不再用“-”隔开,而是变成了两个参数,上面的这个句子与下面的Prolog子句等价。

sentence(S1, S2):-
    nounphrase(S1, S3),
    verbphrase(S3, S2).

因此,既是使用DCG形式定义sentence谓词,我们在调用时仍然需要两个参数。
?- sentence([dog,chases,cat], []).
用DCG来表示词汇只需要使用一个列表:
noun --> [dog].
verb --> [chases].

这两个句子被翻译成:
noun([dog|X], X).
verb([chases|X], X).

就象在本游戏中所需要的那样,有时需要额外的参数来返回语法信息。这个参数只需要简单地加入就行了,而句中纯Prolog则使用{}括起来,这样DCG分析器就不会翻译它。游戏中的复杂的规则将写成如下的形式:

command([V,O]) -->
    verb(Object_Type, V),
    object(Object_Type, O).

verb(place, goto) --> [go, to].
verb(thing, take) --> [take].

object(Type, N) --> det, noun(Type, N).
object(Type, N) --> noun(Type, N).

det --> [the].
det --> [a].

noun(place,X) --> [X], {room(X)}.
noun(place,‘dining room’) --> [dining, room].
noun(thing,X) --> [X], {location(X,_)}.

由于DCG自动的取走第一个参数,如果只输房间名称,前面的子句就不能起作用,所以我们还要加上一条:
command([goto, Place]) --> noun(place, Place).



把形如[goto,office]的命令,转化为goto(office),我们使用称为univ的内部谓词完成这个工作,使用"=…"表示。它的作用如下,把一个谓词转化为了一个列表,或者反过来。

?- pred(arg1,arg2) =… X.
X = [pred, arg1, arg2]


表示OR

load("isJieDi(X,True) <= ((hasType(X, 'Jddaoz')) or (hasType(X, 'Jdx'))) & hasCloseStatus(X,1)")
load("isJieDi(X,True) <= ((nextTo(Y,X)) or (nextToPower(Y,X)) or (nextToBus(Y,X)) or (nextToTransfer(Y,X)) ) & isJieDi(Y,True) & hasCloseStatus(X,1)")

An instance of the pyDatalog.Answer class is returned by pyDatalog.ask("query") and has the following attributes and methods:

* name : name of the predicate that was queried
* arity : arity of the predicate
* answers : a list of tuples that satisfy the query, or True, or None. Each tuple contains as many items as there are variables in the query. If the query leaves some variables unbound, its answer is True. If the query is not satisfiable, its answer is None.
* __eq__(other) : facilitates comparison to another set of tuples
* __str__() : prints the answer

?- X = one, Y = two, write(X-Y).
one - two

因为X-Y实际上是一个数据结构,所以它相对于write来说就只是一个参数。当然其它的数学操作符也能完成相同的功能,例如/。在有些Prolog的版本中干脆引入了“:”这个操作符来专门完成这种任务,有了它我们可以很方便的书写复杂的数据结构了。

object(apple, size:small, color:red, weight:1).
?- object(X, size:small, color:C, weight:W).
X = apple
C = red
W = 1

这里我们使用size:small,代替了原来的size(small),实际上“:”是中缀操作符,它的原始表达形式是:(size,small)。
从这一章所介绍的内容我们可以发现Prolog的程序实际上也是一种数据结构,只不过是使用专门的操作符连接起来的。那么到现在为止,我们所学习过的所有Prolog内容:事实、规则、结构、列表等的实质都是一样的,这也正是Prolog与其它语言的最大区别—程序与数据的高度统一。正是它的这种极其简洁的表达形式,使得它被广泛地应用于人工智能领域。

记录一个bug:
之前是

pyDatalog.create_terms('route,path, X,Y,Z,P,P2,isJieDi, nextTo, powerNextTo, nextToPower, hasType, Line, Power, Bus')

的时候,pyDatalog.load 总会报错“unhashable…”,但是试很多次以后又突然好了,久寻无果,后来发现可能是太啰嗦了,直接改成

pyDatalog.create_terms('path,hasPower, isJieDi')

就不会报错了

X = pyDatalog.ask("isJieDi(S, True)")
for ss in X.answers:
    g.set(( n[ss], n.isJieDi, Literal(True) ))

load('''
route(X, [X,Y], Y) <= nextTo(X,Y)
route(X, [Z] + V1, Y) <= nextTo(X,Z) & route(Z, V1, Y) & ~(Z in V1)
'’’)


# that works
load('''
    (path[X,Y]==P) <= ((path[X,Z]==P2) & nextTo(Z,Y) 
                        & (X!=Y) & (X._not_in(P2)) & (Y._not_in(P2)) 
                        & (P==P2+[Z])) 
    (path[X,Y]==P) <= nextTo(X,Y) & (P==[]) 
''')

pyDatalog 创建和执行datalog语句的方法:

load(‘’'
    + parent(‘Mary’, ‘Jack’)
    + parent(‘Jack’, ‘Lucy’)
    ancestor(X,Y) <= parent(X,Y)
    ancestor(X,Y) <= parent(X,Z) & ancestor(Z,Y)
    ‘’’)

# assert_fact(string, 不限任意参数):添加事实,相当于 ‘+ parent(‘Mary’, ‘Jack')’
assert_fact(‘parent’, ‘Mary’, ‘Jack’)

# ask: 把结果以元祖返回
print(ask(“ancestor(‘Mary’,Y)”))

# retract_fact(string, 不限量任意参数):删除事实, 相当于 ‘-parent(‘Mary’, ‘Jack')’
retract_fact(‘parent’, ‘Mary’,’Jack’)






res=pyDatalog.ask("path['dz_94975FC9-6010-4A79-91DC-0C83CCD81781', Y]==P”)
res.__str__()
res.answers

The aggregate functions are:
    * len_ (P[X]==len_(Y)) <= body : P[X] is the count of values of Y (associated to X by the body of the clause)
    * sum_ (P[X]==sum_(Y, for_each=Z)) <= body : P[X] is the sum of Y for each Z. (Z is used to distinguish possibly identical Y values)
    * min_, max_ (P[X]==min_(Y, order_by=Z)) <= body : P[X] is the minimum (or maximum) of Y sorted by Z.
    * tuple_ (P[X]==tuple_(Y, order_by=Z)) <= body : P[X] is a tuple containing all values of Y sorted by Z.
    * concat_ (P[X]==concat_(Y, order_by=Z, sep=',')) <= body : same as 'sum' but for string. The strings are sorted by Z, and separated by ','.
    * rank_ (P[X]==rank_(group_by=Y, order_by=Z)) <= body : P[X] is the sequence number of X in the list of Y values when the list is sorted by Z.
    * running_sum_ (P[X]==running_sum_(N, group_by=Y, order_by=Z)) <= body : P[X] is the sum of the values of N, for each Y that are before or equal to X when Y's are sorted by Z.
    * mean_ and linear_regression : see our reference

len_(Y)
X._in(P)
X.in_(P)
X._not_in(P)

directTrain(saarbruecken, dudweiler).
directTrain(forbach, saarbruecken).
directTrain(freyming, forbach).
directTrain(stAvold, freyming).
directTrain(fahlquemont, stAvold).
directTrain(metz, fahlquemont).
directTrain(nancy, metz).


travelFromTo(X, Y) :- directTrain(X, Y).
travelFromTo(X, Y) :- directTrain(X, Z), travelFromTo(Z, Y).

n SWI Prolog

?- write_canonical(test[X,Y]).

gives

 []([_,_],test).

It appears to be a compound term, with functor [] with arity 2, aka []/2 with arguments X and Y. I don't understand why it's parsed that way. Datalog may well be different


in prolog, type is predicate.

?- working_directory(_, '/Users/qianruzhou/Documents/code/my/my_prolog').
true.

?- ['db.pl'].
true.

?- [db].
true.

integer constraint (#=)/2 

启动scryer-prolog, 在scryer-prolog文件夹下,找到 target/debug,

192:debug silvia$ scryer-prolog
?-

:- use_module(library(lists)).
:- use_module(library(dcgs)).
:- use_module(library(reif)).
?- use_module(library(lists), [member/2]).
   true.
?- lists:member(X, Xs).
   Xs = [X|_A]
;  Xs = [_A,X|_B]
;  Xs = [_A,_B,X|_C]
;  Xs = [_A,_B,_C,X|_D]

?- use_module(library(lists), [maplist/2]).
   true.
?- maplist(put_char, "Hello, World!\n").
Hello, World!
   true.
posted @   周倩如  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示