1

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

例如,我有这些事实:

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

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

?- father(john, X).

它会返回markand michael,但是我如何比较markand的年龄michael?事实上,我怎么知道查询会返回多个结果呢?

这个想法是我想得到一个父亲的长子

4

2 回答 2

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 将索引这些谓词,这可能足以拯救我们,具体取决于您如何使用它,但它看起来不像是一种可以普遍应用的策略。但是你不能打败这个查询的逻辑阅读:假设我有一个孩子 Child 的父亲父亲,并且假设孩子的年龄是 YoungestAge,那么同一个父亲的 Child2 没有年龄比 YoungestAge 更年轻。

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

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

您将需要一个更大的事实数据库来查看两者之间差异的影响,但这是一个不同的问题。简而言之,setof/3将为您提供唯一答案的排序列表,而bagof/3将为您提供包含所有答案的未排序列表。findall/3做同样的事情,只是处理变量的方式不同。以我的经验,setof/3并且findall/3往往比bagof/3.

如果你正在做的工作需要它,或者如果效率需要它,你可以使用这些元谓词来找到所有可能的解决方案,并遍历列表做你需要做的任何处理。

至于“我怎么知道查询将返回多个结果?”的问题?答案基本上是您可以全部生成它们并查看您有多少 ( findall(..., Answers), length(Answers, Count)),或者您可以使用once/1它们来确保获得一个单一的解决方案。once非常适合使非确定性谓词具有确定性;效果基本上和在子句后面加一个cut是一样的。例如:

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

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

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

一般来说once/1,如果可能,建议您使用过度显式切割。

让我知道这是否有帮助。

于 2013-01-30T07:07:29.740 回答
1

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

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

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

这里的“一个班轮”与上述相同:

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

编辑如果你的 Prolog 有库(聚合),有一种更简洁的方法来获得结果:

eldest(Father, Child) :-
   aggregate(max(A, C), (father(Father, C), age(C, A)), max(_, Child)).
于 2013-01-30T10:21:26.590 回答