5

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

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,_) 是否多次成功,但我不知道该怎么做。

4

3 回答 3

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 个结果后对其进行调整以停止。

于 2012-10-04T18:02:02.213 回答
5
more_than_once(Goal) :-
   \+ \+ call_nth(Goal,2).

此答案call_nth/2中定义的一样。

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

?- more_than_once(重复)。
真的。

?- more_than_once(介于(1,100000,_))。
真的。

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

于 2012-10-07T19:42:26.507 回答
2

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...

于 2012-10-04T18:24:59.683 回答