James Isa
James Isa

Reputation: 55

How to use the "-" constructor in Prolog?

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

Answers (1)

CapelliC
CapelliC

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

Related Questions