Prolog: Determine if the intersection of two lists is empty - prolog

I'm trying to figure out how to determine if the intersection of two lists is empty in Prolog. From what I understand, this is they have no elements in common. I'm new to Prolog(as of last night). Any help is greatly appreciated.
Here is my attempt:
% returns true if head is not a member of List?
intersection([],_).
intersection([Head|Tail],List) :-
\+ member(Head,List),
intersection(Tail,List).
Second Attempt:
?- intersect([A,B,C,D],[E,F,G,H]).
intersect(L1,L2) :-
intersection(L1,L2,[]).
mbratch's resolution solved the problem.
Solution:
?-intersect([a,b,c,d],[e,f,g,h]).
intersect(L1,L2):-
intersection(L1,L2,[]).

A more efficient solution (in general) compared with computing the intersection of the two lists is to fail as soon as a common element is found:
empty_intersection(List1, List2) :-
\+ (member(Element, List1), member(Element, List2)).

Stay pure! It's as easy as 1,2,3:
Using meta-predicate maplist/2 and dif/2 (prolog-dif), we define list_nonmember/2:
list_nonmember(Xs,E) :-
maplist(dif(E),Xs).
Using maplist/2 and list_nonmember/2, we define none_intersect/2:
none_intersect(Xs,Ys) :-
maplist(list_nonmember(Ys),Xs).
Ready to go! Let's run some queries!
?- none_intersect([a,b,c,d],[e,f,g,h]).
true.
?- none_intersect([a,b,c,d],[e,f,a,g,h]).
false.
?- none_intersect([a,b,g,c,d],[e,f,g,h]).
false.

Related

Checking all Members Of List

Let's say that I have the following list in my prolog:
L=[10,11,2,3,5]
Is there a way that we can check all the members of a list L to make sure that each member is less than 5?
We can make use of maplist/2 here. This is a predicate that:
maplist(:Goal, ?List)
True if Goal can successfully be applied on all elements of List. Arguments are reordered to gain performance as well as to make the predicate deterministic under normal circumstances.
So we can here check the elements with:
all_less_five(L) :-
maplist(>(5), L).
Here for every element x ∈ L, it will thus call >(5, x), or in inline form 5 > x. So if all these elements are less than five, the the all_less_five/1 predicate will succeed.
For example:
?- all_less_five([10,11,2,3,5]).
false.
?- all_less_five([2,3,5]).
false.
?- all_less_five([2,3]).
true.
Here goes another solution without using any built-in function:
all_less_five([]).
all_less_five([X|L]):-
X < 5,
all_less_five(L).
This solution uses the typical recursion over lists. The predicate stands true for the empty list, and then we only call the recursion over the tail if the head is less than five.
Here are some questions over the predicate:
?- all_less_five([10,11,2]).
false.
?- all_less_five([2,3,6,5]).
false.
?- all_less_five([1,2,3,4]).
true.
Now it should be easy to implement it to any given X. Try it!

Prolog - remove the non unique elements

I have a predicate to check if the element is member of list and looks the following:
member(X,[X|_]).
member(X,[_|T]) :- member(X,T).
When I called: ?- member(1,[2,3,1,4])
I get: true.
And now I have to use it to write predicate which will remove all non unique elements from list of lists like the following:
remove([[a,m,t,a],[k,a,w],[i,k,b,b],[z,m,m,c]],X).
X = [[t],[w],[i,b,b],[z,c]]
How can I do that?
Using library(reif) for
SICStus|SWI:
lists_uniques(Xss, Yss) :-
maplist(tfilter(in_unique_t(Xss)), Xss, Yss).
in_unique_t(Xss, E, T) :-
tfilter(memberd_t(E), Xss, [_|Rs]),
=(Rs, [], T).
Remark that while there is no restriction how to name a predicate, a non-relational, imperative name often hides the pure relation behind. remove is a real imperative, but we only want a relation. A relation between a list of lists and a list of lists with only unique elements.
An example usage:
?- lists_uniques([[X,b],[b]], [[X],[]]).
dif(X, b).
So in this case we have left X an uninstantiated variable. Therefore, Prolog computes the most general answer possible, figuring out what X has to look like.
(Note that the answer you have accepted incorrectly fails in this case)
Going by your example and #false's comment, the actual problem seems to be something like removing elements from each sublist that occur in any other sublist. My difficulty conceptualizing this into words has led me to build what I consider a pretty messy and gross piece of code.
So first I want a little helper predicate to sort of move member/2 up to lists of sublists.
in_sublist(X, [Sublist|_]) :- member(X, Sublist).
in_sublist(X, [_|Sublists]) :- in_sublist(X, Sublists).
This is no great piece of work, and in truth I feel like it should be inlined somehow because I just can't see myself ever wanting to use this on its own.
Now, my initial solution wasn't correct and looked like this:
remove([Sub1|Subs], [Res1|Result]) :-
findall(X, (member(X, Sub1), \+ in_sublist(X, Subs)), Res1),
remove(Subs, Result).
remove([], []).
You can see the sort of theme I'm going for here though: let's use findall/3 to enumerate the elements of the sublist in here and then we can filter out the ones that occur in the other lists. This doesn't quite do the trick, the output looks like this.
?- remove([[a,m,t,a],[k,a,w],[i,k,b,b],[z,m,m,c]], R).
R = [[t], [a, w], [i, k, b, b], [z, m, m, c]].
So, it starts off looking OK with [t] but then loses the plot with [a,w] because there is not visibility into the input [a,m,t,a] when we get to the first recursive call. There are several ways we could deal with it; a clever one would probably be to form a sort of zipper, where we have the preceding elements of the list and the succeeding ones together. Another approach would be to remove the elements in this list from all the succeeding lists before the recursive call. I went for a "simpler" solution which is messier and harder to read but took less time. I would strongly recommend you investigate the other options for readability.
remove(In, Out) :- remove(In, Out, []).
remove([Sub1|Subs], [Res1|Result], Seen) :-
findall(X, (member(X, Sub1),
\+ member(X, Seen),
\+ in_sublist(X, Subs)), Res1),
append(Sub1, Seen, Seen1),
remove(Subs, Result, Seen1).
remove([], [], _).
So basically now I'm keeping a "seen" list. Right before the recursive call, I stitch together the stuff I've seen so far and the elements of this list. This is not particularly efficient, but it seems to get the job done:
?- remove([[a,m,t,a],[k,a,w],[i,k,b,b],[z,m,m,c]], R).
R = [[t], [w], [i, b, b], [z, c]].
This strikes me as a pretty nasty problem. I'm surprised how nasty it is, honestly. I'm hoping someone else can come along and find a better solution that reads better.
Another thing to investigate would be DCGs, which can be helpful for doing these kinds of list processing tasks.

Prolog no_duplicate function

I'm trying to write a simple procedure that checks if a list has any duplicates. This is what I have tried so far:
% returns true if the list has no duplicate items.
no_duplicates([X|XS]) :- member(X,XS) -> false ; no_duplicates(XS).
no_duplicates([]) :- true.
If I try no_duplicates([1,2,3,3]). It says true. Why is this? I'm probably misunderstanding Prolog here, but any help is appreciated.
To answer your questions: your solution actually fails as expected for no_duplicates([1,2,3,3]). So there is no problem.
Now take the queries:
?- A = 1, no_duplicates([A, 2]).
A = 1.
?- no_duplicates([A, 2]), A = 1.
They both mean the same, so we should expect that Prolog will produce the same answer. (To be more precise we expect the same ignoring errors and non-termination).
However, four proposed solutions differ! And the one that does not, differs for:
?- A = 2, no_duplicates([A, 2]).
false.
?- no_duplicates([A, 2]), A = 2.
Note that it is always the second query that makes troubles. To solve this problem we need a good answer for no_duplicates([A, 2]). It cannot be false, since there are some values for A to make it true. Like A = 1. Nor can it be true, since some values do not fit, like A = 2.
Another possibility would be to issue an instantiation_error in this case. Meaning: I have not enough information so I better stop than mess around with potentially incorrect information.
Ideally, we get one answer that covers all possible solutions. This answer is dif(A, 2) which means that all A that are different to 2 are solutions.
dif/2 is one of the oldest built-in predicates, already Prolog 0 did possess it. Unfortunately, later developments discarded it in Prolog I and thus Edinburgh Prolog and thus ISO Prolog.
However, current systems including SICStus, YAP, SWI all offer it. And there is a safe way to approximate dif/2 safely in ISO-Prolog
no_duplicates(Xs) :-
all_different(Xs). % the common name
all_different([]).
all_different([X|Xs]) :-
maplist(dif(X),Xs).
all_different(Xs).
See: prolog-dif
Here's yet another approach, which works because sort/2 removes duplicates:
no_duplicates(L) :-
length(L, N),
sort(L, LS),
length(LS, N).
I'd go at the problem more descriptively:
no_duplicates( [] ) . % the empty list is unique
no_duplicates( [X|Xs] ) :- % a list of length 1+ is unique
\+ member(X,Xs) , % - if its head is not found in the tail,
no_duplicates(Xs) % - and its tail is itself unique.
. %
Thinking on this, since this is a somewhat expensive operation — O(n2)? — it might be more efficient to use sort/2 and take advantage of the fact that it produces an ordered set, removing duplicates. You could say something like
no_duplicates( L ) :-
sort(L,R) , % sort the source list, removing duplicates
length(L,N) , % determine the length of the source list
length(R,N) . % check that against the result list
Or you could use msort/3 (which doesn't remove duplicates), might be a bit faster, too:
no_duplicates( L ) :-
msort(L,R), % order the list
\+ append(_,[X,X|_],R) % see if we can find two consecutive identical members
.
Duplicates in a list are same elements not at the same place in the list, so no_duplicates can be written :
no_duplicates(L) :-
\+((nth0(Id1, L, V), nth0(Id2, L, V), Id1 \= Id2)).
Jay already noted that your code is working. An alternative, slightly less verbose
no_duplicates(L) :- \+ (append(_, [X|XS], L), memberchk(X, XS)).

What exactly is member(b,X)?

Today I came across this query:
?- member(b,X).
The program was:
member(X,[X|_]).
member(X,[_|T]) :-
member(X,T),
!.
When I ran the query, I got these answers:
?- member(b,X).
X = [b|_G1560] ;
X = [_G1559, b|_G1563].
What exactly is that? What does this query do?
The query member(b,X) generates lists containing b. As the second argument is not instantiated, you have a (theoretically) infinite number of solutions. The first solution will have b in the first position, the second solution will have b in the second position and so on. Moreover, if you look closely to any of the solutions, you see that it represents any list with a b on that position. For example, the first solution is [b| _]. As the list tail is not instantiated (see the member/2 predicate base case), this solution unifies with any list with b in the head position.
If you want to make the member/2 deterministic, i.e. if you want to use the predicate only to check if a term is a member of a list, you will need to add a cut in the base clause, not in the recursive clause as #false noted:
member(Head, [Head| _]) :-
!.
member(Head, [_| Tail]) :-
member(Head, Tail).
The resulting predicate is usually named memberchk/2 and available as such as a library predicate.

Check If Everything In Head Is Less Than Tail

Given a list containing sublists [[1].[2],[3]] how would I check to see if HEAD of the first sublist in the list is less than the rest of the HEADS of the other sublists?
Comparison of Standard Order of Terms in ISO-Prolog can be applied - recursively - to arbitrary complex structures.
Then your problem could be solved with something like
first_head_is_less([H|R]) :- maplist(#<(H), R).
test:
?- first_head_is_less([[1],[2],[3]]).
true.
?- first_head_is_less([[10],[2],[3]]).
false.
edit the code above must be refined, because it fail (for instance) on this:
?- first_head_is_less([[1,2],[1,3],[3]]).
true.
which is incorrect. Here a stricter test:
first_head_is_less([H|R]) :-
maplist(head_is_less(H), R).
head_is_less([F|_], [E|_]) :- F #< E.

Resources