Verifying intersection of two lists in Prolog - prolog

I am trying to check whether the interaction provided is the correct intersection of two lists. Example query list:
?- intersectionL([1,2,3,4],[1,3,5,6],[1,3]).
/* expected output: true. */
?- intersectionL([1,2,3,4],[1,3,5,6],X).
/* expected output: X = [1,3]. */
?- intersectionL([1,2,3],[4,3],[3]).
/* expected output: true. */
I have written a function to calculate the intersection of two lists (intersectionL). From there I want to verify whether the third argument given is the same as the intersection I found (intersectionHelper).
intersectHelper([], []).
intersectHelper([H1|T1], [H2|T2]):-
H1 = H2, intersectHelper(T1, T2).
intersectionL([], X, []).
intersectionL([H1|T1], L2, [H1|Res]):-
member(H1, L2), intersectionL(T1, L2, Res).
intersectionL([X|T1], L2, Res):-
intersectionL(T1, L2, Res).
I am having two problems: Firstly, how do I use intersectionHelper within intersectionL. Secondly, how do I output the list of intersections if no list is provided (like in query 2 above).
Edit: I was able to solve it for anyone else interested. I do not require intersectionHelper.
intersectionL([], X, []).
intersectionL([H1|T1], L2, [H1|Res]):-
member(H1, L2), intersectionL(T1, L2, Res).
intersectionL([X|T1], L2, Res):-
intersectionL(T1, L2, Res).

Not an answer but there I note that there is a RED CUT missing in intersectionL/3:
For example, computing the intersection of [1,2,3] and [1,4,5] gives two solutions, only one of which is desired:
?- intersectionL([1,2,3],[1,4,5],L).
L = [1] ; % desired
L = []. % not desired
We need to add a guard condition in the second clause:
intersectionL([], X, []).
intersectionL([H1|T1], L2, [H1|Res]):-
member(H1, L2), intersectionL(T1, L2, Res).
intersectionL([X|T1], L2, Res):-
\+member(H1, L2), intersectionL(T1, L2, Res).
or use a "red cut" to tell Prolog to not try the third clause if the second succeeded (this is ugly):
intersectionL([], X, []).
intersectionL([H1|T1], L2, [H1|Res]):-
member(H1, L2),!,intersectionL(T1, L2, Res).
intersectionL([X|T1], L2, Res):-
intersectionL(T1, L2, Res).
Best is to have an explicit guard with a green cut to shed any choicepoints that are of no use, while still staying declarative:
intersectionL([], _, []).
intersectionL([H1|T1], L2, [H1|Res]):-
member(H1, L2),!,intersectionL(T1, L2, Res).
intersectionL([H1|T1], L2, Res):-
\+member(H1, L2),!,intersectionL(T1, L2, Res).
Now it works, one solution only.
?- intersectionL([1,2,3],[1,4,5],L).
L = [1].

Related

Ensure Input List Minimum Length Prolog

I have a predicate that is true when two lists are identitcal, save that their first and last elements are swapped.
A requirement, however is that the predicate is UNTRUE when there are < 2 elements in the input List.
swap_ends([], []).
swap_ends(L1, L2) :-
append([H1 | B1], [H2], L1),
append([H2 | B1], [H1], L2).
This is correct for all instances where |inputList| >= 2, but also yields things like.
swap_ends([12345],L).
L = [-1231].
But we would expect no value for L.
I have tried substituting:
swap_ends([], []).
for
swap_ends([A], [A]).
But this ends with an obvious fail state for some cases.
A simple solution reusing your code is:
swap_ends([X1, X2| Xs], [Y1, Y2| Ys]) :-
swap_ends_aux([X1, X2| Xs], [Y1, Y2| Ys])
swap_ends_aux([], []).
swap_ends_aux(L1, L2) :-
append([H1| B1], [H2], L1),
append([H2| B1], [H1], L2).

Getting the head and tail of a list

I know if I do:
[H|T] = [a,b,c,d].
H = a,
T = [b,c,d].
What I'm trying to do is get the head and tail of a list inside a rule. Is this possible? I'm setting up base cases for a recursive call but for now I'd be satisfied if it just returned L3 as a append of the first list head and the second list head. I'm just not sure how I can get the head and list.
compose([], L1, L1).
compose(L2, [], L2).
compose([], [], []).
compose(L1, L2, L3) :-
[H1|T1] = L1,
[H2|T2] = L2,
append(H1, H2, L3).
I've also tried doing below based on what I've seen online:
compose([], L1, L1).
compose(L2, [], L2).
compose([], [], []).
compose([H1|T1], [H2|T2], L3) :-
append(H1, H2, L3).
but the trace fails on the Call to this predicate.In a successful case I would like it to do the following:
compose([a,b,c], [d,e,f], L).
L = [a, d].
at the very least for now.
compose([X|_],[Y|_], [X,Y]).
It is not more than that. Maybe you want to add cases for empty lists:
compose([], [], []).
compose([X|_], [], [X]).
compose([], [X|_], [X]).

Remove duplicate elements from list

This is my code
removevowels(L1, L2) :-
removevowels(L1, L2, []).
removevowels([], [], _).
removevowels([X|L1], [X|L2], Aux) :-
consonant(X),
not(member(X, Aux)),
removevowels(L1, L2, [X|Aux]).
removevowels([X|L1], L2, Aux) :-
not(consonant(X)),
removevowels(L1, L2, Aux).
If i run this:
?- removevowels([a,m,m,n], X).
It should print
X = [m, n]
but it's giving false and if i run this
?- removevowels([a,m,n], X).
X = [m,n]
It's alright when it doesn't have repeated elements.
Auxiliar predicates used:
member(X, [X|_]).
member(X, [_|Tail]) :-
member(X, Tail).
consonant(b)
consonant(c), etcetc ....
What's wrong in my code?
The best is to replace not/1 by the ISO (\+)/1 first.
For debugging, the first thing you would do is to minimize the problem. E.g., the query
?- removevowels([m,m],X).
is just as bad. But much smaller. So what are your rules for consonants? There is a single rule:
removevowels([X|L1], [X|L2], Aux) :-
consonant(X),
\+member(X, Aux),
removevowels(L1, L2, [X|Aux]).
So consonants have to occur only once, the next occurrence makes this fail already.
Should you still not be sure why the query fails, you might also want to generalize the query. In stead of seeing that removevowels([m,m],X) fails, you might ask
?- removevowels([m,Y],X).
which means: Is there any Y such that there is a solution. However, this method only works, if your program is "relational". In your case the last rule, however prevents this:
removevowels([X|L1], L2, Aux) :-
\+consonant(X),
removevowels(L1, L2, Aux).
It will never succeed with X being an uninstantiated variable. I'd rather use instead:
removevowels([X|L1], L2, Aux) :-
vowel(X),
removevowels(L1, L2, Aux).
Back to consonants:
What you are missing is either a separate rule for consonants that are already present, or some "defaulty" if-then-else.
Further, this extra checking might not be the most effective way to handle this. Maybe just extract the vowels first, and then sort/2 them.
in second clause, there are two 2 conditions in join, while the last clause has only one. I would commit the good case with a cut, and let the last clause only act as a trusted skip clause:
removevowels([], [], _).
removevowels([X|L1], [X|L2], Aux) :-
consonant(X),
not(member(X, Aux)),
!, removevowels(L1, L2, [X|Aux]).
removevowels([_X|L1], L2, Aux) :-
removevowels(L1, L2, Aux).
using if/then/else construct for the last 2 clauses, we avoid the problem noted by false:
removevowels([], [], _).
removevowels([X|L1], R, Aux) :-
( consonant(X),
not(member(X, Aux))
-> R=[X|L2],
removevowels(L1, L2, [X|Aux])
; removevowels(L1, R, Aux)
).

Prolog variable gets no value

I've written the following code in prolog:
contains(L1, []).
contains(L1, [X | T2]) :- member(X, L1), contains(L1, T2).
minus(L, [], L).
minus(L1, L2, L3) :- contains(L1, L3), nomembers(L3, L2).
nomembers(L1, []).
nomembers(L1, [X | T2]) :- not(member(X, L1)), nomembers(L1, T2).
contains(L1, L2) returns true if all of the members in L2 appear in L1. (contains([1,2],[1,1,1]) is true).
minus(L1, L2, L3) returns true if L3=L1\L2, meaning L3 consists of members of L1 but not of L2.
When I ask minus([1,2,3,4],[2,1],L), I get the answer that L=[], although logically it should be L=[3,4]. Does someone know why?
Above comment of mbratch is very helpful.
Notice, that your current minus(L1, L2, L3) definition is: All members of L3 are in L1 and no member from L3 is in L2.
Prolog is giving you good answer with L3 = [], it fits for definition I wrote above.
EDIT: Below code should do what you want, but currently I don't have prolog on my computer, so I can't test it.
remove(X, [X|T], T) :- !.
remove(X, [H|T], [H|T2]) :- remove(X, T, T2).
minus(L1,[],L1).
minus(L1,[H|T2],T3) :- member(H, L1), !, remove(H,L1,L4), minus(L4, T2, T3).
minus(L1,[H|T2],[H|T3]) :- minus(L1, T2, T3).
remove(X,LA,LB) which says: LB is LA without it first occurrence of X, so it's just removing element from the list.

Prolog finding and removing predicate

I my have code:
locdiff([A|T], [A|_], T).
locdiff([H|T], L2, [H|T2]) :-
locdiff(T, L2, T2).
and when i test it with locdiff([(a,1), (b,2), (b,3), (c,3), (c,4)], [(b,_)], L3), it only finds and removes one of the [(b,_)] which is (b,2). I need it to find and remove both (b,2) and (b,3) or what ever the [(b,_)] contains. can anyone help me with what i have missed?
there is a technical complication that's worth to note if you follow larsman' hint, that I would implement straight in this way
locdiff([], _, []).
locdiff([A|T], [A|_], R) :-
!, locdiff(T, [A|_], R).
locdiff([H|T], L2, [H|T2]) :-
locdiff(T, L2, T2).
with this
?- locdiff([(a,1), (b,2), (b,3), (c,3), (c,4)], [(b,_)], L3).
L3 = [ (a, 1), (b, 3), (c, 3), (c, 4)].
you can see that the first instance is removed, and the last. That's because the first match binds the anonymous variable, and then forbids following matchings, except the last (b,_)
Then a completed procedure would read
locdiff([], _, []).
locdiff([H|T], [A|_], R) :-
\+ \+ H = A, % double negation allows matching without binding
!, locdiff(T, [A|_], R).
locdiff([H|T], L2, [H|T2]) :-
locdiff(T, L2, T2).
now the outcome is what you are requiring.
Alternatively, you need to be more precise in pattern matching, avoiding undue binding
locdiff([], _, []).
locdiff([(A,_)|T], [(A,_)|_], R) :-
!, locdiff(T, [(A,_)|_], R).
locdiff([H|T], L2, [H|T2]) :-
locdiff(T, L2, T2).
?- locdiff([(a,1), (b,2), (b,3), (c,3), (c,4)], [(b,_)], L3).
L3 = [ (a, 1), (c, 3), (c, 4)].
Please note that some library has specific functionality, like exclude/3 in SWI-Prolog, but still you need attention to avoid bindings:
eq([(E,_)|_], (E,_)).
locdiff(L, E, R) :-
exclude(eq(E), L, R).
May be you need something loke that :
locdiff([], _, []).
locdiff([(b,_)|T], [(b,_)], T1) :-
!, locdiff(T, [(b,_)], T1).
locdiff([H|T], L2, [H|T2]) :-
locdiff(T, L2, T2).
But why do you write [A| _] if there is only one element in the list ?
[EDIT] I forgot the ! in the second rule

Resources