我有一个这样的事实数据库:
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,_)是否不止一次成功,但我不知道该怎么做。
答案 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 ......