I have a database of facts like this:
li(a,2).
li(b,3).
li(b,1).
li(c,2).
li(d,1).
li(d,1).
I need to write a predicate more(+Let) that succeeds if it exists more than one fact li(Let,_).
For example the queries more(b) and more(d) will succeed, but more(a) and more(c) will not. My idea was to check if li(Let,_) succeeds more than once, but I do not know how to do it.
Try findall/3
:
findall(X, li(d,X), L), length(L,N), N>1.
Abstracting the d
out and making a predicate is trivial. Right? :)
If you don't want to use any of the predicates like findall
, you can change the representation of your knowledge - bring it down one level, so to speak:
my_knowledge(li, [a-2,b-3,b-1,c-2,d-1,d-1]).
and then you can use SWI Prolog's predicate select/3
to handle it:
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 = [].
You can rewrite the last predicate as basic recursion over lists, and then tweak it to stop after getting first N results.
more_than_once(Goal) :-
\+ \+ call_nth(Goal,2).
With call_nth/2
as defined in this answer.
The big advantage of this solution compared to the other solutions proposed is that it will succeed rapidly even if there is a very large sequence of answers. In fact, it will even succeed for an infinite sequence of answers:
?- more_than_once(repeat). true. ?- more_than_once(between(1,100000,_)). true.
(The implementation of call_nth/2
uses some non-standard, low-level built-ins of SWI. It is possible to avoid that, but with even more headache.)
SWI-Prolog has library(aggregate).
:- [library(aggregate)].
more(Key) :- aggregate_all(count, li(Key, _), C), C > 1.
test:
?- more(b).
true.
?- more(a).
false.
It's not very easy to learn, but useful to handle such common tasks. If you have a very large code base, then findall (and aggregate as well, that uses findall inside) could be inefficient, building a list only to count its elements.
Then you could use a side effect based predicate: in this related answer you'll find such utility. For max efficiency, see the comments, where is explained how to use nb_setval/nb_getval...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With