在windows下用swipl命令运行prolog脚本时,经常会遇到下面的错误:
ERROR: char_code/2: Cannot represent due to `character_code'
解决方法有两种:
1、使用swipl-win命令替代swipl命令
2、在macos下使用swipl命令
应该是一个bug,在读入字符时(如;),处理不当导致的。
Learn and share.
在windows下用swipl命令运行prolog脚本时,经常会遇到下面的错误:
ERROR: char_code/2: Cannot represent due to `character_code'
解决方法有两种:
1、使用swipl-win命令替代swipl命令
2、在macos下使用swipl命令
应该是一个bug,在读入字符时(如;),处理不当导致的。
1、看一下SWI-Prolog的安装说明,发现命令行工具在下面的路径
/Applications/SWI-Prolog.app/Contents/MacOS
2、修改PATH变量,添加SWI-Prolog命令行工具在下面的路径
#查看命令行工具 cd /Applications/SWI-Prolog.app/Contents/MacOS ls #查看PATH变量 echo "$PATH" #修改PATH变量 vi $HOME/.bash_profile #增加一行 export PATH=${PATH}:/Applications/SWI-Prolog.app/Contents/MacOS
3、重启命令行
#查看PATH变量 echo "$PATH" #测试一下 swipl hello.pl
列表:
放在用方括号中的一组项目的集合,各项目之间使用逗号分割。
空表:
没有项目的列表,用[]表示。
表头与表尾:
[X|Y]可以与任意的列表匹配,匹配成功后,X绑定为列表的第一个项目的值,我们称之为表头(head)。
而Y则绑定为剩下的列表,我们称之为表尾(tail)。
表尾(tail)一定是列表,而表头(head)则是一个项目,该项目可以是表,也可以是其他的任何数据结构。
看下这个例子就清楚了:
1 ?- [a|[b,c,d]] = [a,b,c,d]. true. 2 ?- [a|b,c,d] = [a,b,c,d]. ERROR: Syntax error: Unexpected comma or bar in rest of list ... 3 ?- [H|T] = [a]. H = a, T = []. 4 ?- [H|T] = [a,b,c,d]. H = a, T = [b, c, d]. 5 ?- [H|T] = [a,[b,c,d]]. H = a, T = [[b, c, d]]. 6 ?- [H|T] = []. false. 7 ?- [A,B|T] = [a,b,c,d]. A = a, B = b, T = [c, d]. 8 ?- [a|[b|[c|[d|[]]]]] = [a,b,c,d]. true.
检查数据是否存在:
1 ?- member(a, [a,b,c]). true . 2 ?- member(d, [a,b,c]). false. 3 ?- member(d, [a,b,c,[d],e]). false. 4 ?- member([d], [a,b,c,[d],e]). true . 5 ?- member(X, [a,b,c]). X = a ; X = b ; X = c.
追加
1 ?- append([a,b,c],[d,e,f],X). X = [a, b, c, d, e, f]. 2 ?- append([],[d,e,f],X). X = [d, e, f]. 3 ?- append([a,b],Y,[a,b,c,d]). Y = [c, d]. 4?- append(X,Y,[a,b,c]). X = [], Y = [a, b, c] ; X = [a], Y = [b, c] ; X = [a, b], Y = [c] ; X = [a, b, c], Y = [] ;
删除
1 ?- delete([a,b,c,d,e], c, X). X = [a, b, d, e]
联合(Unification)
变量与任何项目: 变量可以与任何项目绑定,其中也包括变量
原始项目与原始项目: 两个原始项目(原子或整数)只有当它们相同时才能联合。
结构与结构: 如果两个结构的每个相应的参数能联合,那么这两个结构可以联合。
1 ?- (1,2,3)=(X,Y,Z). X = 1, Y = 2, Z = 3. 2 ?- (1,2,3)=(X,X,Z). false. 3 ?- (1,1,3)=(X,X,Z). X = 1, Z = 3. 4 ?- (1,X,3)=(X,1,Z). X = 1, Z = 3. 5 ?- (1,X,3)=(X,2,Z). false.
1 ?- (1,2,X)=(1,2,(3,4,5)). X = (3, 4, 5). 2 ?- (1,2,X)=(1,2,(3,4,Y)),Y=5. X = (3, 4, 5), Y = 5. %_表示不关心匹配内容 3 ?- (1,2,X,Y,7)=(1,2,(3,4,5),6,_). X = (3, 4, 5), Y = 6. 4 ?- X = Y, Y = hi, write(X). hi X = Y, Y = hi.
%swipl -s objs.pl %Hansen %room room(kitchen). %objects and location %object(Name, Color, Size, Weight). location(object(candle, red, small, 1), kitchen). location(object(apple, red, small, 1), kitchen). location(object(apple, green, small, 1), kitchen). location(object(table, blue, big, 50), kitchen). %current room here(kitchen). %can take something? can_take(Thing) :- here(Room), location(object(Thing, _, small, _), Room). can_take(Thing) :- here(Room), location(object(Thing, _, big, _), Room), write('The '), write(Thing), write(' is too big to carry.'), nl, fail. can_take(Thing) :- here(Room), not(location(object(Thing, _, _, _), Room)), write('There is no '), write(Thing), write(' here.'), nl, fail. %out put the weight write_weight(1) :- write('1 pound'). write_weight(W) :- W > 1, write(W), write(' pounds'). %list all things in a room list_things(Place) :- location(object(Thing, Color, Size, Weight), Place), write('A '),write(Size),tab(1), write(Color),tab(1), write(Thing), write(', weighing '), write_weight(Weight), nl, fail.
动态修改全局数据
asserta(X) :把子句X当作此子句的谓词的第一个子句加入到动态数据库中,不可回溯。
assert(X)或assertz(X) :把子句X当作此子句的谓词的最后一个子句加入到动态数据库中,不可回溯。
retract(X) :把子句X从动态数据库中删除,不可回溯。
%swipl -s room.pl %Hansen :-dynamic here/1. :-dynamic location/2. :-dynamic bag/1. %房间定义 room(kitchen). room(office). room(hall). room(diningroom). room(cellar). %门定义 door(office, hall). door(kitchen, office). door(hall, diningroom). door(kitchen, cellar). door(diningroom, kitchen). %规则:有门的两个房间是相通的 connect(X,Y) :- door(X,Y). connect(X,Y) :- door(Y,X). %物品在哪个房间 location(desk, office). location(apple, kitchen). location(flashlight, desk). location(washingmachine, cellar). location(nani, washingmachine). location(broccoli, kitchen). location(crackers, kitchen). location(computer, office). %背包 bag(tourch). %哪些物品可以吃 edible(apple). edible(crackers). edible(broccoli). %当前位置 here(hall). %移动 move(Place):- can_go(Place),retract(here(X)), asserta(here(Place)). can_go(Place):- here(X), connect(X, Place). can_go(Place):- write('You can''t go to '),write(Place),write(' from here.'), nl, fail. %获取物品 take(X):- can_take(X), retract(location(X,_)), asserta(bag(X)), write(X), write(' taken.'), nl. can_take(Thing):- here(Place), location(Thing, Place). can_take(Thing):- write('There is no '), write(Thing), write(' here.'), nl, fail. %放下物品 put(X):- can_put(X), here(Place), retract(bag(X)), asserta(location(X,Place)), write(X), write(' put.'), nl. can_put(Thing):- bag(Thing). can_put(Thing):- write('There is no '), write(Thing), write(' in your bag.'), nl, fail. %房间物品列表 list_things(Place):- location(X, Place),tab(2),write(X),nl,fail. list_things(_). %相连的 list_connections(Place):- connect(Place, X),tab(2),write(X),nl,fail. list_connections(_). %持有物品列表 list_bag(Thing):- bag(X),tab(2),write(X),nl,fail. list_bag(_). %吃东西 eat(Thing):- can_eat(Thing), retract(bag(X)), write(Thing), write(' eaten. Yummy!'). can_eat(Thing):- bag(Thing), edible(Thing). can_eat(Thing):- not(bag(Thing)), write('There is no '), write(Thing), write(' in your bag.'), nl, fail. can_eat(Thing):- bag(Thing), write('You can''t eat the '), write(Thing), write('.'), nl, fail. %查看房间情况 look :- here(Place), write('You are in the '), write(Place), nl, write('You can see:'),nl,list_things(Place), write('You can go to:'), nl, list_connections(Place), write('You have:'),nl,list_bag(Thing). %帮助 game :- write('Look around: look/0'),nl, write('Move around: move/1'),nl, write('Take something: take/1'),nl, write('Eat something: eat/1').
1 ?- game. Look around: look/0 Move around: move/1 Take something: take/1 Eat something: eat/1 true. 2 ?- look. You are in the hall You can see: You can go to: diningroom office You have: tourch true. 3 ?- move(diningroom). true . 4 ?- look. You are in the diningroom You can see: You can go to: kitchen hall You have: tourch true. 5 ?- move(kitchen). true . 6 ?- look. You are in the kitchen You can see: apple broccoli crackers You can go to: office cellar diningroom You have: tourch true. 7 ?- take(apple). apple taken. true . 8 ?- look. You are in the kitchen You can see: broccoli crackers You can go to: office cellar diningroom You have: apple tourch true. 9 ?- eat(apple). apple eaten. Yummy! true .
递归定义都包括两个部分:边界条件与递归部分。
边界条件定义最简单的情况。而递归部分,则首先解决一部分问题,然后再调用其自身来解决剩下的部分,
每一次都将进行边界检测,如果剩下的部分已经是边界条件中所定义的情况时,那么递归就圆满成功了。
递归解决阶乘问题
%swipl -s factorial.pl %Hansen fun(N):- N>0,fact(N,1). fun(N):- write(N), write(' is smaller than 1. '),fail. fact(1,M):- write(M),!. fact(N,M):- N1 is N-1, M1 is M*N, fact(N1,M1).
1 ?- fun(0). 0 is smaller than 1. false. 2 ?- fun(1). 1 true . 3 ?- fun(2). 2 true . 4 ?- fun(3). 6 true . 5 ?- fun(4). 24 true . 6 ?- fun(5). 120 true . 7 ?- fun(6). 720 true .
递归解决汉诺塔问题
%swipl -s hanoi.pl %Hansen %N个盘子从A到C的问题,用递归解决的思路: %N-1个盘子,从A到B %1个盘子,从A到C %N-1个盘子,从B到C hanoi(N):-move(N,a,b,c). move(1,A,_,C):-fromto(A,C),!. move(N,A,B,C):-N1 is N-1,move(N1,A,C,B),fromto(A,C),move(N1,B,A,C). fromto(Loc1,Loc2):-nl,write('move one disk from '),write(Loc1),write(' to '),write(Loc2).
执行查询:
1 ?- hanoi(1). move one disk from a to c true. 2 ?- hanoi(2). move one disk from a to b move one disk from a to c move one disk from b to c true. 3 ?- hanoi(3). move one disk from a to c move one disk from a to b move one disk from c to b move one disk from a to c move one disk from b to a move one disk from b to c move one disk from a to c true. %这里就会出错啦 4 ?- hanoi(0). ERROR: Out of local stack
数学计算: X is <数学表达式> 数学运算: +-*/ () 比较运算: X > Y X < Y X >= Y X =< Y
1 ?- X is 1*2+3/4-(5-6)*7. X = 9.75. 2 ?- 3=<4. true. 3 ?- 3>=4. false. 4 ?- 3>4. false. 5 ?- 3<4. true. 6 ?- X is 2+2, 3>=X. false.
看一下摄氏度与华氏度互转的程序:
%swipl -s calc.pl %Hansen c_to_f(C,F) :- F is C*9/5+32. f_to_c(F,C) :- C is (F-32)*5/9.
1 ?- c_to_f(11,F). F = 51.8. 2 ?- f_to_c(51.8,F). F = 10.999999999999998.
1、常用输出函数:
write:把它的参数作为字符串输出到屏幕上。
从Call端口调用时总是成功的,从Redo端口回溯时总是失败的。
nl:在屏幕上输出一个回车符。
从Call端口调用时总是成功的,从Redo端口回溯时总是失败的。
tab:输出n个空格,n为它的参数(整数)。
从Call端口调用时总是成功的,从Redo端口回溯时总是失败的。
2、启用调试
?- debug.
3、and,or,not
and:两个条件之间,用“,”分割
or:两个条件之间,用“;“分割
not:我使用的版本中,有not函数
比如,下面的例子中,like(X):-(fruit(X);vegetable(X)),not(meat(X)),not(hate(X)).:
panpan喜欢吃的食物逻辑为:
a、喜欢蔬菜和水果
b、不喜欢吃肉
c、有些蔬菜不喜欢
d、其余不喜欢
%swipl -s fruit.pl %Hansen %and(A,B):- A, B. %or(A,B):- A; B. %not(A):- call(A), !, fail. like(X):-(fruit(X);vegetable(X)),not(meat(X)),not(hate(X)). fruit(apple). fruit(grape). fruit(pear). meat(pork). meat(mutton). meat(beef). vegetable(tomato). vegetable(cabbage). vegetable(spinach). vegetable(celery). hate(celery).
所以,panpan不喜欢羊肉,喜欢大白菜,但不喜欢芹菜
1 ?- like(mutton). false. 2 ?- like(cabbage). true. 3 ?- like(celery). false.
通过上一章,大家可以知道,prolog是根据初始数据+逻辑规则,来进行逻辑判断一门语言。
程序员的重点,从如何进行逻辑判断,变成了如何制定逻辑规则。
我们了解一下prolog的一些基础知识:
% swipl -s hello.pl % author neohope %输入数据,like左边是人,右边是爱好 %请注意"." like(wangwu,fishing). like(wangwu,smoking). like(wangwu,binglang). like(jiliang,riding). like(jiliang,smoking). like(jiliang,soccer). like(guosir,soccer). like(linlin,riding). like(linlin,smoking). like(zhaowen,smoking). like(hansen,sleep). %描述关系:如果路人甲X,与路人乙Y,同时喜欢一种项目时Z,他们可以一起玩(paly) %请注意"." play(X,Y):-like(X,Z),like(Y,Z).
1、参数,参数可以是以下四种之一:
整数(integer):绝对值小于某一个数的正数或负数。
原子(atom):由小写字母开头的字符串。
变量(variable):由大写字母或下划线(_)开头。
结构(structure):在以后的章节介绍。
可用字符:A-Z;a-z;0-9;+-/\^,.~:.?#$等。
如:wangwu,fishing两个都是参数
2、谓词(predicate):Prolog语言的基本组成元素,可以是一段程序、一个数据类型或者是一种关系。
它由谓词名和参数组成。两个名称相同而参数的数目不同的谓词是不同的谓词。
如:like(arg1,arg2)只是一个谓词,like是谓词名,arg1,arg2是参数。
3、事实(facts):prolog中最简单的谓词。类似于关系数据库中的记录。
如:like(wangwu,fishing).这就是一个事实,wangwu喜欢钓鱼。
4、目标(goal):Prolog的查询工作是靠模式匹配完成的,查询的模板叫做目标。
如果有某个事实与目标匹配,那么查询就成功了,Prolog的解释器会回显’true.’。
如果没有匹配的事实,查询就失败了,解释器回显’false.’。
5、联合(unification):Prolog的模式匹配工作称为联合。
当数据库中只包括事实时,以下三个条件是使联合成功的必要条件。
a、目标谓词名与数据库中的某个谓词名相同。
b、这两个谓词的参数数目相同。
c、所有的参数也相同。
如:?- like(wangwu,riding).这个查询,因为没有like(wangwu,riding)这个事实,返回false。
6、绑定(binding):在使用带有变量的查询时(首字母大写),使用变量可以和任何的条目匹配。
联合成功之后,变量的值将和它所匹配的条目的值相同,这叫做变量的绑定。
当带变量的目标成功的和数据库中的事实匹配之后,Prolog将返回变量绑定的值。
如:?- like(wangwu,X).会返回wangwu喜欢的所有项目。
7、端口:Prolog的目标有四个端口用来控制运行的流程:调用(call)、退出(exit)、重试(redo)以及失败(fail)。
每个端口的功能如下:
call 开始使用目标搜寻子句。
exit 目标匹配成功,在成功的子句上作记号,并绑定变量。
redo 试图重新满足目标,首先释放变量,并从上次的记号开始搜索。
fail 表示再找不到更多的满足目标的子句了。
一开始使用Call端口进入目标,如果匹配成功就到了exit端口,如果失败就到了fail端口,如果用户输入分号,则又从redo端口进入目标。
8、规则:储存起来的查询。它的语法如下:head :- body,其中,
head 是谓词的定义部分,与事实一样,也包括谓词名和谓词的参数说明。
:- 连接符,一般可以读作‘如果’。
body 一个或多个目标。
如:play(X,Y):-like(X,Z),like(Y,Z).
head:play(X,Y)
:-
body:这个body有两个目标,like(X,Z),like(Y,Z)
规则名:play/2
描述:如果路人甲X,与路人乙Y,同时喜欢一种项目时Z,他们可以一起玩(paly)
最近看了下prolog,整理如下:
比起c,java,C#来,prolog与sql的逻辑更相似一些:
a、输入初始数据(sql建表,及表中数据输入)
b、描述关系(sql存储过程)
c、进行查询(查询表,调用存储过程)
d、只需要告诉prolog,需要查什么,不关心如何去查询(比如sql如何查找数据,是db服务器的问题,我们一般不关心如何具体实现)
比如,首先进行a,b两步:
% swipl -s hello.pl % author neohope %输入数据,like左边是人,右边是爱好 %请注意"." like(wangwu,fishing). like(wangwu,smoking). like(wangwu,binglang). like(jiliang,riding). like(jiliang,smoking). like(jiliang,soccer). like(guosir,soccer). like(linlin,riding). like(linlin,smoking). like(zhaowen,smoking). like(hansen,sleep). %描述关系:当路人甲X,与路人乙Y,同时喜欢一种项目时Z,他们可以一起玩(paly) %请注意"." play(X,Y):-like(X,Z),like(Y,Z).
然后进行查询:
%查询wangwu,喜欢的项目 %请注意".",";"要手工输入 1 ?- like(wangwu,X). X = fishing ; X = smoking ; X = binglang. %查询喜欢riding项目的人 %请注意".",";"要手工输入 2 ?- like(X,riding). X = jiliang ; X = linlin. %查询,jiliang与lilin是否喜欢一起玩 3 ?- play(jiliang,linlin). true . %查询,jiliang与hansen是否喜欢一起玩 4 ?- play(jiliang,hansen). false. %为什么呢?查一下hansen喜欢什么运动 5 ?- like(hansen,X). X = sleep. %原来他只喜欢睡觉^_^,不喜欢运动,当然没人和他玩咯 有些概念没?那咱们进入下一章。