Prolog - 如何检查谓词是否多次成功

时间:2012-10-04 17:22:12

标签: prolog

我有一个这样的事实数据库:

li(a,2).
li(b,3).
li(b,1).
li(c,2).
li(d,1).
li(d,1).

如果它存在多个事实li(Let,_),我需要写一个更多的谓词(+ Let)成功。

例如,查询更多(b)和更多(d)将成功,但更多(a)和更多(c)将不会。 我的想法是检查li(Let,_)是否不止一次成功,但我不知道该怎么做。

3 个答案:

答案 0 :(得分:6)

尝试findall/3

findall(X, li(d,X), L), length(L,N), N>1.

提取d并制作谓词是微不足道的。对? :)


如果您不想使用findall之类的任何谓词,您可以更改知识的表示形式 - 将其降低到一个级别,可以这么说:

my_knowledge(li, [a-2,b-3,b-1,c-2,d-1,d-1]).

然后你可以使用SWI Prolog的谓词select/3来处理它:

select_knowledge(kn, key, R):-
  my_knowledge(kn,L),
  select_key(L,key,R).

select_key(L,K,R):-
  select(K-X,L,L1) -> R=[X|R1], select_key(L1,K,R1)
  ; R = [].

您可以将最后一个谓词重写为列表上的基本递归,然后在获得前N个结果后将其调整为停止。

答案 1 :(得分:5)

more_than_once(Goal) :-
   \+ \+ call_nth(Goal,2).

this answer中定义的call_nth/2

与其他提出的解决方案相比,此解决方案的一大优势是即使存在非常大的答案序列,它也能快速成功。事实上,它甚至会成功获得无限的答案:

?- more_than_once(repeat).
true.

?- more_than_once(between(1,100000,_)).
true.

call_nth/2的实现使用了SWI的一些非标准的低级内置函数。可以避免这种情况,但更令人头疼。)

答案 2 :(得分:2)

SWI-Prolog有图书馆(aggregate)。

:- [library(aggregate)].

more(Key) :- aggregate_all(count, li(Key, _), C), C > 1.

试验:

?- more(b).
true.

?- more(a).
false.

学习起来不是很容易,但对处理这些常见任务很有用。如果你有一个非常大的代码库,那么findall(以及使用findall里面的聚合)可能效率低下,只建立一个列表来计算它的元素。

然后你可以使用基于副作用的谓词:在this相关答案中你会发现这样的效用。为了获得最大效率,请参阅注释,其中说明了如何使用nb_setval / nb_getval ......