Remove duplicate elements from list - prolog

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

Related

Prolog combinatoric of two lists ignoring the last items

I'm trying to get the combinatorics of two lists ignoring the last element of both.
the code:
isady(_, [_]) :-
write('stop here'),
nl,
false.
isady(E, [H|T]) :-
write(E),
write('-'),
write(H),
nl,
isady(E, T).
ady([_], _) :-
write('stop here'),
nl,
false.
ady([H|T], L2) :-
isady(H, L2);
ady(T, L2).
output:
?- ady([a,b,c], [d,e,f]).
a-d
a-e
stop here
a-f
b-d
b-e
stop here
b-f
stop here
c-d
c-e
stop here
c-f
expected output:
?- ady([a,b,c], [d,e,f]).
a-d
a-e
b-d
b-e
As you can see, it doesn't stop when it has to, does anyone knows why?
Try this:
isady(_, [_]) :-
write('stop here'),
nl,
false.
isady(E, [H|T]) :-
T\=[],
write(E),
write('-'),
write(H),
nl,
isady(E, T).
ady([_], _) :-
write('stop here'),
nl,
false.
ady([H|T], L2) :-
T\=[],
isady(H, L2);
ady(T, L2).
The new condition T \= [] will prevent the use of the last elements of both lists.
[EDIT] You can also explicitly use the pattern for a list of length at least 2, to avoid the execution of the "step case" clause with lists of length 1:
isady(_, [_]).
isady(E, [X,Y|T]) :-
writeln(E-X),
isady(E,[Y|T]).
ady([_], _).
ady([X,Y|T], L) :-
isady(X, L),
ady([Y|T], L).
Result:
?- ady([a,b,c], [d,e,f]).
a-d
a-e
b-d
b-e
true ;
false.

Verifying intersection of two lists in 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].

Prolog predicates involving lists

I want to write a predicate that finds a particular element in a list, by comparing the lists. For example the data I could have for instance could be like:
likes(phil, [apple, banana, orange]).
likes(hetti, [apple, cherry, grapes]).
etc.
I just want someone to explain how something like this would work, because I've been trying to use member or select; but it seems to be more difficult to find information on this. Could I use pattern matching?
It seems likely that you want to know what the common likes are between phil and hetti. If so, then this is the code:
?- bothlike(phil, hetti, Ls), write(Ls), nl.
bothlike(X, Y, Ls) :-
likes(X, Xs),
likes(Y, Ys),
intersection(Xs, Ys, Ls).
likes(phil, [apple, banana, orange]).
likes(hetti, [apple, cherry, grapes]).
intersection([], _, []).
intersection([H1|T1], L2, L3) :-
member(H1, L2),
!,
L3 = [H1|T3],
intersection(T1, L2, T3).
intersection([_|T1], L2, L3) :-
intersection(T1, L2, L3).
It gives the answer [apple].

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