我有一个这样的事实数据库:
li(a,2).
li(b,3).
li(b,1).
li(c,2).
li(d,1).
li(d,1).
如果存在多个事实 li(Let,_),我需要编写一个成功的谓词 more(+Let)。
例如查询 more(b) 和 more(d) 会成功,但 more(a) 和 more(c) 不会。我的想法是检查 li(Let,_) 是否多次成功,但我不知道该怎么做。
我有一个这样的事实数据库:
li(a,2).
li(b,3).
li(b,1).
li(c,2).
li(d,1).
li(d,1).
如果存在多个事实 li(Let,_),我需要编写一个成功的谓词 more(+Let)。
例如查询 more(b) 和 more(d) 会成功,但 more(a) 和 more(c) 不会。我的想法是检查 li(Let,_) 是否多次成功,但我不知道该怎么做。
尝试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 个结果后对其进行调整以停止。
more_than_once(Goal) :-
\+ \+ call_nth(Goal,2).
与此答案call_nth/2
中定义的一样。
与提出的其他解决方案相比,此解决方案的最大优势在于,即使存在非常大的答案序列,它也会迅速成功。事实上,它甚至会成功获得无限的答案序列:
?- more_than_once(重复)。 真的。 ?- more_than_once(介于(1,100000,_))。 真的。
(实现call_nth/2
使用了一些非标准的、低级的 SWI 内置。可以避免这种情况,但更令人头疼。)
SWI-Prolog 有库(聚合)。
:- [library(aggregate)].
more(Key) :- aggregate_all(count, li(Key, _), C), C > 1.
测试:
?- more(b).
true.
?- more(a).
false.
它不是很容易学习,但对处理这些常见任务很有用。如果你有一个非常大的代码库,那么 findall (以及聚合,它使用 findall 内部)可能效率低下,构建一个列表只是为了计算它的元素。
然后你可以使用基于副作用的谓词:在这个相关的答案中,你会发现这样的实用程序。为了获得最大效率,请参阅注释,其中解释了如何使用 nb_setval/nb_getval...