比较查询返回的结果

时间:2013-01-30 06:12:40

标签: prolog

当我运行查询时,它可以返回零,一个或多个结果。如何“保存”这些结果以便我以后可以比较它们,或者我如何在运行中比较它们?

例如,我有这些事实:

father(john, mark).
father(john, michael).
age(michael, 10).
age(mark, 12).

现在,如果我运行这样的查询

?- father(john, X).

它会返回markmichael,但如何比较markmichael的年龄?实际上,我怎么能知道查询会返回多个结果?

我的想法是,我想得到父亲的长子

2 个答案:

答案 0 :(得分:2)

你在这里遇到的事实是,在回溯时,Prolog会释放它在产生前一个结果时发现的变量绑定。这个功能在早期引起了很多惊愕,所以知道你并不孤单,我们都曾经在那里。

你应该做的第一件事是尽量放弃告诉Prolog如何获得结果的需要,而是专注于告诉Prolog如何区分你想要的逻辑结果。毕竟这是逻辑编程。 :)当你说“我怎样才能比较markmichael的年龄?”你隐藏了一个真实的问题,假设一旦你知道如何抓住事物,你就可以自己找到答案。但你不想自己找到答案,你希望Prolog找到它!

我们举一个例子。假设你想知道谁是谁中最小的孩子。你可以逻辑地做到这一点:

?- father(Father, Child), 
   age(Child, YoungestAge), 
   \+ (father(Father, Child2), 
       age(Child2, Younger), 
       Younger < YoungestAge).
Father = john,
Child = michael,
YoungestAge = 10.

遗憾的是,这对于大型数据库来说效率很低,因为Prolog必须搜索每个age/2事实以找到特定父级的所有子级。据推测,Prolog会将这些谓词编入索引,这可能足以根据您的使用方式来拯救我们,但它看起来并不像您可以普遍应用的策略。但是你无法击败这个问题的逻辑阅读:假设我有一个父亲孩子的父亲,并且假设孩子的年龄是最年轻的,那么同一个父亲的孩子2的年龄比最年轻的年轻。

通常,您确实需要所有解决方案,因此有三个谓词:setof/3bagof/3findall/3。它们都具有基本相同的API,语义略有不同。例如,如果您想要父母的所有孩子,您可以使用setof来获取它们:

?- setof(Child, father(john, Child), Children).
Children = [mark, michael].

你需要一个更大的事实数据库才能看到两者之间差异的影响,但这是一个不同的问题。简而言之,setof/3将为您提供唯一答案的排序列表,而bagof/3将为您提供包含所有答案的未排序列表。 findall/3做同样的事情,它对待变量的方式不同。根据我的经验,setof/3findall/3的使用频率往往超过bagof/3

如果您正在进行的工作需要它,或者效率需要它,您可以使用这些元谓词来查找所有可能的解决方案,并在列表中执行您需要执行的任何处理。

关于“我怎么能知道查询会返回多个结果?”的问题。答案基本上是你可以生成它们并查看你有多少(findall(..., Answers), length(Answers, Count)),或者你可以使用once/1来确保你得到一个解决方案。 once非常适合使非确定性谓词具有确定性;效果与在条款之后立即进行切割基本相同。例如:

?- father(john, X).
X = mark ;
X = michael.

?- once(father(john, X)).
X = mark.

?- father(john, X), !.
X = mark.

一般情况下,如果可能,建议您使用once/1而不是显式剪切。

如果有帮助,请告诉我。

答案 1 :(得分:1)

只是丹尼尔回答的一个脚注:

eldest(Father, Child) :-
  findall(A/C, (father(Father, C), age(C, T), A is -T), L),
  sort(L, [_/Child|_]).

我使用否定年龄的技巧以相反的顺序获取列表。

这里的'one liner'与上面的内容完全相同:

eldest(Father, Child) :-
  setof(A/C, T^(father(Father, C), age(C, T), A is -T), [_/Child|_]).
如果您的Prolog有库(aggregate),

编辑,有一种更简洁的方式来获得结果:

eldest(Father, Child) :-
   aggregate(max(A, C), (father(Father, C), age(C, A)), max(_, Child)).