Prolog 人工智能语言中文论坛---打造优质Prolog学习交流园地

一个供Prolog爱好者学习与交流的地方


您没有登录。 请登录注册

在一个句子中求出所有的解---好用的bagof、setof、findall谓词

浏览上一个主题 浏览下一个主题 向下  留言 [第1页/共1页]

假设我们有以下3个事实:

代码:
fact('The apple is red').
fact('The sky is blue').
fact('The banana is yellow').
并且定义:

代码:
print_fact :- fact(X), write(X), nl, fail.

则当我们查询print_fact时,确实可以列出所有的事实:

The apple is red
The sky is blue
The banana is yellow
false.

虽然3个事实都列了出来,但其实这是透过成功write出、用fail强迫失败、成功write出、用fail强迫失败、成功write出来实现的,
这3个解分散在3次不同的回溯中,我们无法对这3个解进行统一操作。
如果我们希望统一操作这3个解,必须将所有满足查询的解装进同一个List。
很多时候会需要用到这个功能,比如一个棋局游戏,
电脑可能必须在某一句程序句子里,将所有可能的走法穷举出来,
挑选出评分最高的策略作为它的下一步,
这时必须将所有可能的策略的分数摆进同一个List,
这些策略才能方便的被比较,由List中挑选分数最高的作为电脑的最终决策。

bagof谓词提供了这个功能,
如果我查询 bagof(X,fact(X),Result_list).
则传回 Result_list = ['The apple is red', 'The sky is blue', 'The banana is yellow'].
也就是,所有满足fact(X)这个条件的解,都被装进了Result_list这个List中了。

bagof的第一个参数是你关注的解的表现形式、一个模板,
比如,如果你改成查询 bagof(X+X,fact(X),Result_list).
则传回
Result_list = ['The apple is red'+'The apple is red', 'The sky is blue'+'The sky is blue', 'The banana is yellow'+'The banana is yellow'].
查询 bagof(you_should_know(X),fact(X),Result_list).
则传回
Result_list = [you_should_know('The apple is red'), you_should_know('The sky is blue'), you_should_know('The banana is yellow')].
查询 bagof([1,X],fact(X),Result_list).
则传回
Result_list = [[1, 'The apple is red'], [1, 'The sky is blue'], [1, 'The banana is yellow']].

这样意思应该很明白了吧?

当然也可以关注不只一个变量,像是查询
bagof([X,Y],lovers(X,Y),lovers_list).
能够得到一个列出所有的情侣的清单:lovers_list,
形如[[Jack,Rose],[Peter,Wendy],[Steven,Lucy]]。


与bagof功能相近的是setof与findall谓词,
setof与bagof的差别在于,它会将传回的List里重复的解去掉,并且排序。
需要用到setof的时候是,有时你并不想知道所有的解是什么,
你只想知道有哪些解的形式,
比如解集合[1, 1, 1, 5, 3, 3, 2]与集合[1, 2, 3, 5]可能在你的程序里意义上是等价的。

而findall与bagof的差别是:
若找不到解,findall传回[](空List),而bagof传回fail(失败)。



findall与bagof或setof还有一个非常明显的差异:

代码:
fact(a, i).
fact(a, j).
fact(a, k).

fact(b, w).
fact(b, x).
fact(b, y).
fact(b, z).

查询?- bagof(Q, fact(P, Q), List).
传回
P = a,
List = [i, j, k] ;
P = b,
List = [w, x, y, z].

而查询?- findall(Q, fact(P, Q), List).
传回
List = [i, j, k, w, x, y, z].

看出差异了吗?因为你的模版参数(bagof的第1个参数)里没有P,
bagof会将P分组传回对应的解,而findall不会,
也就是说,bagof第一次只传回[i, j, k],
你打";"问prolog还有无其他解时,
它才会再传回[w, x, y, z],
如果你要的是不分组的效果,你应该用findall。

顺带一提,如果这里改成查询 bagof(Q, P^fact(P, Q), List),
意思是我不想绑定P(不想分组显示),
那就跟查询findall(Q, fact(P, Q), List)得到的结果相同了。
这里看不出在bagof的第2个参数里加一个"P^"有什么独特的效果,
但有时你会想用setof配上这个功能,
因为你用findall,findall并不会将传回的解集合List中的重复项剔除,
那我就单纯用setof吧,setof总会剔除重复项了吧?但setof又会将解集合分组显示,
当我不想分组显示但却又想剔除重复项时,就需要用到setof配上这段讲的"P^"的功能了。


更细节的用法,请参阅help(bagof).

查阅用户资料 http://prolog.longluntan.net

浏览上一个主题 浏览下一个主题 返回页首  留言 [第1页/共1页]

您在这个论坛的权限:
不能在这个论坛回复主题