您在这里遇到的事实是,在回溯时,Prolog 会释放它在产生先前结果时找到的变量绑定。这个功能在早期会引起很多恐慌,所以要知道你并不孤单,我们都曾经在那里。
你应该做的第一件事是试着放下告诉 Prolog 如何得到结果的需要,而专注于告诉 Prolog 如何在逻辑上区分你想要的结果。毕竟这是逻辑编程。:) 当你说“我如何比较和的年龄mark
?michael
” 您将真正的问题隐藏在假设一旦您知道如何抓住事物之后,您就可以自己找到答案。但是您不想自己找到答案,您希望 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/3
:bagof/3
和findall/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
,如果可能,建议您使用过度显式切割。
让我知道这是否有帮助。