Reputation: 55
So I need to create a Prolog predicate that takes an input that looks like this [true-X, false-Y, false-X, true-Z]
and only return the variables that occur once. So for this example, it would return [true-Z]
since Z only occurs once. I have been able to do this with just normal lists.
singles([],[]).
singles([H | T], L) :-
member(H, T),
delete(T, H, Y),
singles( Y, L).
singles([H | T], [H|T1]) :-
\+member(H, T),
singles(T, T1).
If I run this then it returns
?- singles([1,1,2,3,4,3,3,2], R).
R = [4]
since it only returns the values that appear once in the list. The problem with what I'm trying to do is that I can't use the member
or delete
predicates with the "-" constructor. Basically, I have to start by splitting each item into it's two parts and then just compare the variable singles([Pol-Var | T], L)
. To compare the two variables, I created an occurs
predicate that compares the variable at the head of the list.
occurs(X, [Pol-Var|T]) :- X == Var.
Here's what I have so far.
singles([],[]).
singles([Pol-Var | T], L) :-
occurs(Var, T),
singles(T, L).
singles([Pol-Var | T], [Pol-Var|T1]) :-
\+occurs(Var, T),
singles(T, T1).
occurs(X, [Pol-Var|T]) :- X == Var.
What this does is basically like if I had the input [1,1,2,3,2]
then the output would be [1,2,3,2]
so it just removes any duplicates that are right beside eachother. So if I had the input [true-X, false-X, false-Y, true-Y, true-Z]
then the output would be [false-X, true-Y, true-Z]
and I want it to be [true-Z]
. How can I do that?
Upvotes: 3
Views: 202
Reputation: 60014
As Daniel pointed out in his first comment, the real problem you're facing is the unwanted unification performed by Prolog between the arguments of such builtins like member/2 or delete/3. An old trick-of-the-trade
of the Prolog community is to use double negation to achieve matching without unification, but as we'll see, this would not help you too much.
The simpler way to solve your problem, seems to rewrite member/2 and delete/3, so a possibility could be:
singles([],[]).
singles([H | T], L) :-
member_(H, T),
delete_(T, H, Y),
singles(Y, L).
singles([H | T], [H | T1]) :-
\+member_(H, T),
singles(T, T1).
member_(_-H, [_-T|_]) :- H == T, !.
member_(E, [_|R]) :- member_(E, R).
delete_([], _, []).
delete_([_-T|Ts], F-H, Rs) :- T == H, !, delete_(Ts, F-H, Rs).
delete_([T|Ts], H, [T|Rs]) :- delete_(Ts, H, Rs).
that yields
?- singles([true-X, false-Y, false-X, true-Z],S).
S = [false-Y, true-Z]
You can see you underspecified your requirements: from your test case, seems we should delete every occurrence of false-VAR
irrespectively of VAR...
Upvotes: 3