Related
My confusion mainly lies around understanding singleton variables.
I want to implement the predicate noDupl/2 in Prolog. This predicate can be used to identify numbers in a list that appear exactly once, i. e., numbers which are no duplicates. The first argument of noDupl is the list to analyze. The
second argument is the list of numbers which are no duplicates, as described below.
As an example, for the list [2, 0, 3, 2, 1] the result [0, 3, 1] is computed (because 2 is a duplicate).
In my implementation I used the predefined member predicate and used an auxiliary predicate called helper.
I'll explain my logic in pseudocode, so you can help me spot where I went wrong.
First off, If the first element is not a member of the rest of the list, add the first element to the new result List (as it's head).
If the first element is a member of T, call the helper method on the rest of the list, the first element H and the new list.
Helper method, if H is found in the tail, return list without H, i. e., Tail.
noDupl([],[]).
noDupl([H|T],L) :-
\+ member(H,T),
noDupl(T,[H|T]).
noDupl([H|T],L) :-
member(H,T),
helper(T,H,L).
helper([],N,[]).
helper([H|T],H,T). %found place of duplicate & return list without it
helper([H|T],N,L) :-
helper(T,N,[H|T1]).%still couldn't locate the place, so add H to the new List as it's not a duplicate
While I'm writing my code, I'm always having trouble with deciding to choose a new variable or use the one defined in the predicate arguments when it comes to free variables specifically.
Thanks.
Warnings about singleton variables are not the actual problem.
Singleton variables are logical variables that occur once in some Prolog clause (fact or rule). Prolog warns you about these variables if they are named like non-singleton variables, i. e., if their name does not start with a _.
This convention helps avoid typos of the nasty kind—typos which do not cause syntax errors but do change the meaning.
Let's build a canonical solution to your problem.
First, forget about CamelCase and pick a proper predicate name that reflects the relational nature of the problem at hand: how about list_uniques/2?
Then, document cases in which you expect the predicate to give one answer, multiple answers or no answer at all. How?
Not as mere text, but as queries.
Start with the most general query:
?- list_uniques(Xs, Ys).
Add some ground queries:
?- list_uniques([], []).
?- list_uniques([1,2,2,1,3,4], [3,4]).
?- list_uniques([a,b,b,a], []).
And add queries containing variables:
?- list_uniques([n,i,x,o,n], Xs).
?- list_uniques([i,s,p,y,i,s,p,y], Xs).
?- list_uniques([A,B], [X,Y]).
?- list_uniques([A,B,C], [D,E]).
?- list_uniques([A,B,C,D], [X]).
Now let's write some code! Based on library(reif) write:
:- use_module(library(reif)).
list_uniques(Xs, Ys) :-
list_past_uniques(Xs, [], Ys).
list_past_uniques([], _, []). % auxiliary predicate
list_past_uniques([X|Xs], Xs0, Ys) :-
if_((memberd_t(X,Xs) ; memberd_t(X,Xs0)),
Ys = Ys0,
Ys = [X|Ys0]),
list_past_uniques(Xs, [X|Xs0], Ys0).
What's going on?
list_uniques/2 is built upon the helper predicate list_past_uniques/3
At any point, list_past_uniques/3 keeps track of:
all items ahead (Xs) and
all items "behind" (Xs0) some item of the original list X.
If X is a member of either list, then Ys skips X—it's not unique!
Otherwise, X is unique and it occurs in Ys (as its list head).
Let's run some of the above queries using SWI-Prolog 8.0.0:
?- list_uniques(Xs, Ys).
Xs = [], Ys = []
; Xs = [_A], Ys = [_A]
; Xs = [_A,_A], Ys = []
; Xs = [_A,_A,_A], Ys = []
...
?- list_uniques([], []).
true.
?- list_uniques([1,2,2,1,3,4], [3,4]).
true.
?- list_uniques([a,b,b,a], []).
true.
?- list_uniques([1,2,2,1,3,4], Xs).
Xs = [3,4].
?- list_uniques([n,i,x,o,n], Xs).
Xs = [i,x,o].
?- list_uniques([i,s,p,y,i,s,p,y], Xs).
Xs = [].
?- list_uniques([A,B], [X,Y]).
A = X, B = Y, dif(Y,X).
?- list_uniques([A,B,C], [D,E]).
false.
?- list_uniques([A,B,C,D], [X]).
A = B, B = C, D = X, dif(X,C)
; A = B, B = D, C = X, dif(X,D)
; A = C, C = D, B = X, dif(D,X)
; A = X, B = C, C = D, dif(D,X)
; false.
Just like my previous answer, the following answer is based on library(reif)—and uses it in a somewhat more idiomatic way.
:- use_module(library(reif)).
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
tpartition(=(V), Vs, Equals, Difs),
if_(Equals = [], Xs = [V|Xs0], Xs = Xs0),
list_uniques(Difs, Xs0).
While this code does not improve upon my previous one regarding efficiency / complexity, it is arguably more readable (fewer arguments in the recursion).
In this solution a slightly modified version of tpartition is used to have more control over what happens when an item passes the condition (or not):
tpartition_p(P_2, OnTrue_5, OnFalse_5, OnEnd_4, InitialTrue, InitialFalse, Xs, RTrue, RFalse) :-
i_tpartition_p(Xs, P_2, OnTrue_5, OnFalse_5, OnEnd_4, InitialTrue, InitialFalse, RTrue, RFalse).
i_tpartition_p([], _P_2, _OnTrue_5, _OnFalse_5, OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse):-
call(OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse).
i_tpartition_p([X|Xs], P_2, OnTrue_5, OnFalse_5, OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse):-
if_( call(P_2, X)
, call(OnTrue_5, X, CurrentTrue, CurrentFalse, NCurrentTrue, NCurrentFalse)
, call(OnFalse_5, X, CurrentTrue, CurrentFalse, NCurrentTrue, NCurrentFalse) ),
i_tpartition_p(Xs, P_2, OnTrue_5, OnFalse_5, OnEnd_4, NCurrentTrue, NCurrentFalse, RTrue, RFalse).
InitialTrue/InitialFalse and RTrue/RFalse contains the desired initial and final state, procedures OnTrue_5 and OnFalse_5 manage state transition after testing the condition P_2 on each item and OnEnd_4 manages the last transition.
With the following code for list_uniques/2:
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
tpartition_p(=(V), on_true, on_false, on_end, false, Difs, Vs, HasDuplicates, []),
if_(=(HasDuplicates), Xs=Xs0, Xs = [V|Xs0]),
list_uniques(Difs, Xs0).
on_true(_, _, Difs, true, Difs).
on_false(X, HasDuplicates, [X|Xs], HasDuplicates, Xs).
on_end(HasDuplicates, Difs, HasDuplicates, Difs).
When the item passes the filter (its a duplicate) we just mark that the list has duplicates and skip the item, otherwise the item is kept for further processing.
This answer goes similar ways as this previous answer by #gusbro.
However, it does not propose a somewhat baroque version of tpartition/4, but instead an augmented, but hopefully leaner, version of tfilter/3 called tfilter_t/4 which can be defined like so:
tfilter_t(C_2, Es, Fs, T) :-
i_tfilter_t(Es, C_2, Fs, T).
i_tfilter_t([], _, [], true).
i_tfilter_t([E|Es], C_2, Fs0, T) :-
if_(call(C_2,E),
( Fs0 = [E|Fs], i_tfilter_t(Es,C_2,Fs,T) ),
( Fs0 = Fs, T = false, tfilter(C_2,Es,Fs) )).
Adapting list_uniques/2 is straightforward:
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
if_(tfilter_t(dif(V),Vs,Difs), Xs = [V|Xs0], Xs = Xs0),
list_uniques(Difs, Xs0).
Save scrollbars. Stay lean! Use filter_t/4.
You have problems already in the first predicate, noDupl/2.
The first clause, noDupl([], []). looks fine.
The second clause is wrong.
noDupl([H|T],L):-
\+member(H,T),
noDupl(T,[H|T]).
What does that really mean I leave as an exercise to you. If you want, however, to add H to the result, you would write it like this:
noDupl([H|T], [H|L]) :-
\+ member(H, T),
noDupl(T, L).
Please look carefully at this and try to understand. The H is added to the result by unifying the result (the second argument in the head) to a list with H as the head and the variable L as the tail. The singleton variable L in your definition is a singleton because there is a mistake in your definition, namely, you do nothing at all with it.
The last clause has a different kind of problem. You try to clean the rest of the list from this one element, but you never return to the original task of getting rid of all duplicates. It could be fixed like this:
noDupl([H|T], L) :-
member(H, T),
helper(T, H, T0),
noDupl(T0, L).
Your helper/3 cleans the rest of the original list from the duplicate, unifying the result with T0, then uses this clean list to continue removing duplicates.
Now on to your helper. The first clause seems fine but has a singleton variable. This is a valid case where you don't want to do anything with this argument, so you "declare" it unused for example like this:
helper([], _, []).
The second clause is problematic because it removes a single occurrence. What should happen if you call:
?- helper([1,2,3,2], 2, L).
The last clause also has a problem. Just because you use different names for two variables, this doesn't make them different. To fix these two clauses, you can for example do:
helper([H|T], H, L) :-
helper(T, H, L).
helper([H|T], X, [H|L]) :-
dif(H, X),
helper(T, X, L).
These are the minimal corrections that will give you an answer when the first argument of noDupl/2 is ground. You could do this check this by renaming noDupl/2 to noDupl_ground/2 and defining noDupl/2 as:
noDupl(L, R) :-
must_be(ground, L),
noDupl_ground(L, R).
Try to see what you get for different queries with the current naive implementation and ask if you have further questions. It is still full of problems, but it really depends on how you will use it and what you want out of the answer.
I am trying to build a list function in prolog which will hopefully do the following;
split(1, 4, [1, 2, 3, 4]). [2, 3]
split(2, 4, [1, 2, 3, 4, 5]). [3]
That is it will put all the items in a list which appear in between the two value provided.
What have I tried;
split(Start, Finish, List) :- append(List, _, [Start|Xs]),
append([Finish|Xs], _, List).
I can just never seem to get it working! I am new to prolog so please be relatively kind!!
Thanks
EDIT
Ok so I have a solution and would like to know if it could be improved. The solution is below,
% Split a list at a specified index
split(List, Index, Front, Back) :-
length(Front, Index),
append(Front, Back, List).
% Get list items inbetween members
inbetween(List, From, To, Result) :-
nth1(FromI, List, From),
nth0(ToI, List, To),
split(List, FromI, _, List1),
split(List, ToI, _, List2),
subtract(List1, List2, Result).
As you can see I followed the advice in the comments and tweaked it a little. Are there any improvements to this?
Thanks, (Could it even be possible in one predicate?)
EXAMPLE
inbetween([1,2,3,4,5,6], 2, 5, Result). % [3,4]
inbetween([a,b,c,d,e,f], a, e, Result). % [b,c,d]
I think the solution you came up with is interesting and it only needs a small adjustment to make it work correctly:
% Split a list at a specified index
split(List, Index, Front, Back) :-
length(Front, Index),
append(Front, Back, List).
% Get list items inbetween members
inbetween(List, From, To, Result) :-
nth1(FromI, List, From),
split(List, FromI, _, List1),
nth0(ToI, List1, To),
split(List1, ToI, Result, _).
The split/4 predicate is unchanged from what you have. The inbetween/4 main predicate I modified a little so that first it finds everything after the From, then it uses that result and finds everything before the To yielding the final result.
| ?- inbetween([a,b,c,a,x,b,e,f], a, b, L).
L = [] ? ;
L = [b,c,a,x] ? ;
L = [x] ? ;
(1 ms) no
A shorter version, using append/3 would be:
betwixt2(List, A, B, Result) :-
append(_, [A|T], List),
append(Result, [B|_], T).
Another approach which is more recursively based and not using library calls would be:
inbetween(List, A, B, Result) :-
split_left(List, A, R),
split_right(R, B, Result).
split_left([X|T], X, T).
split_left([_|T], X, R) :- split_left(T, X, R).
split_right([X|_], X, []).
split_right([H|T], X, [H|R]) :- split_right(T, X, R).
And finally, there's an interesting, concise solution, I hadn't considered when making my comments, using a DCG which is more transparent:
betwixt(A, B, M) --> anything, [A], collect(M), [B], anything.
anything --> [].
anything --> [_], anything.
collect([]) --> [].
collect([H|T]) --> [H], collect(T).
inbetween(List, A, B, Result) :- phrase(betwixt(A, B, Result), List).
The DCG in this case nicely spells out exactly what's happening, with the same results as above. For brevity, I could also use collect(_) in place of anything in the first clause, but didn't want to waste the unused argument.
To use a nice notation credited to #false, we can use ... as a term as shown below:
betwixt(A, B, M) --> ..., [A], collect(M), [B], ... .
... --> [].
... --> [_], ... .
collect([]) --> [].
collect([H|T]) --> [H], collect(T).
I have a strange problem that I do not know how to solve.
I have written a predicate that compresses lists by removing repeating items.
So if the input is [a,a,a,a,b,c,c,a,a], output should be [a,b,c,a]. My first code worked, but the item order was wrong. So I add a append/3 goal and it stopped working altogether.
Can't figure out why. I tried to trace and debug but don't know what is wrong.
Here is my code which works but gets the item order wrong:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
p08([H2|T], [H1|O], X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
Here's the newer version, but it does not work at all:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
append(H1, O, N),
p08([H2|T], N, X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
H1 is not a list, that's why append(H1, O, N) fails.
And if you change H1 to [H1] you actually get a solution identical to your first one. In order to really reverse the list in the accumulator you should change the order of the first two arguments: append(O, [H1], N). Also, you should change the first rule with one that matches the empty list p08([], X, X) (without it, the goal p08([], [], Out) fails).
Now, to solve your problem, here is the simplest solution (which is already tail recursive, as #false stated in the comments to this answer, so there is no need for an accumulator)
p([], []). % Rule for empty list
p([Head, Head|Rest], Out):- % Ignore the Head if it unifies with the 2nd element
!,
p([Head|Rest], Out).
p([Head|Tail], [Head|Out]):- % otherwise, Head must be part of the second list
p(Tail, Out).
and if you want one similar to yours (using an accumulator):
p08(List, Out):-p08(List, [], Out).
p08([], Acc, Acc).
p08([Head, Head|Rest], Acc, Out):-
!,
p08([Head|Rest], Acc, Out).
p08([Head|Tail], Acc, Out):-
append(Acc, [Head], Acc2),
p08(Tail, Acc2, Out).
Pure and simple:
list_withoutAdjacentDuplicates([],[]).
list_withoutAdjacentDuplicates([X],[X]).
list_withoutAdjacentDuplicates([X,X|Xs],Ys) :-
list_withoutAdjacentDuplicates([X|Xs],Ys).
list_withoutAdjacentDuplicates([X1,X2|Xs],[X1|Ys]) :-
dif(X1,X2),
list_withoutAdjacentDuplicates([X2|Xs],Ys).
Sample query:
?- list_withoutAdjacentDuplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a] ; % succeeds, but leaves useless choicepoint(s) behind
false
Edit 2015-06-03
The following code is based on if_/3 and reified term equality (=)/3 by #false, which---in combination with first argument indexing---helps us avoid above creation of useless choicepoints.
list_without_adjacent_duplicates([],[]).
list_without_adjacent_duplicates([X|Xs],Ys) :-
list_prev_wo_adj_dups(Xs,X,Ys).
list_prev_wo_adj_dups([],X,[X]).
list_prev_wo_adj_dups([X1|Xs],X0,Ys1) :-
if_(X0 = X1, Ys1 = Ys0, Ys1 = [X0|Ys0]),
list_prev_wo_adj_dups(Xs,X1,Ys0).
Let's see it in action!
?- list_without_adjacent_duplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
In this answer we use meta-predicate foldl/4 and
Prolog lambdas.
:- use_module(library(apply)).
:- use_module(library(lambda)).
We define the logically pure predicatelist_adj_dif/2 based on if_/3 and (=)/3:
list_adj_dif([],[]).
list_adj_dif([X|Xs],Ys) :-
foldl(\E^(E0-Es0)^(E-Es)^if_(E=E0,Es0=Es,Es0=[E0|Es]),Xs,X-Ys,E1-[E1]).
Let's run the query given by the OP!
?- list_adj_dif([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
How about a more general query? Do we get all solutions we expect?
?- list_adj_dif([A,B,C],Xs).
A=B , B=C , Xs = [C]
; A=B , dif(B,C), Xs = [B,C]
; dif(A,B), B=C , Xs = [A,C]
; dif(A,B), dif(B,C), Xs = [A,B,C].
Yes, we do! So... the bottom line is?
Like many times before, the monotone if-then-else construct if_/3 enables us to ...
..., preserve logical-purity, ...
..., prevent the creation of useless choicepoints (in many cases), ...
..., and remain monotone—lest we lose solutions in the name of efficiency.
More easily:
compress([X],[X]).
compress([X,Y|Zs],Ls):-
X = Y,
compress([Y|Zs],Ls).
compress([X,Y|Zs],[X|Ls]):-
X \= Y,
compress([Y|Zs],Ls).
The code works recursevely and it goes deep to the base case, where the list include only one element, and then it comes up, if the found element is equal to the one on his right , such element is not added to the 'Ls' list (list of no duplicates ), otherwise it is.
compr([X1,X1|L1],[X1|L2]) :-
compr([X1|L1],[X1|L2]),
!.
compr([X1|L1],[X1|L2]) :-
compr(L1,L2).
compr([],[]).
I am given 2 lists for example K=[a,b,c,d,e,f,g] and L=[a,b,1,d,e,2,g]. When these 2 lists have 2 different elements, then they are friendly.
This is what I've tried:
friendly(K,L):-
append(L1,[Z],A1),
append(A1,L2,A2),
append(A2,[Q],A3),
append(A3,L3,K),
append(L1,[Y],B1),
append(B1,L2,B2),
append(B2,[W],B3),
append(B3,L3,L),
Z\=Y,
Q\=W.
Thank you all so much, at last I found the correct code:
friend(L1,L2):-
append(A,Y,L1),
append([Z|T],[F|TT],Y),
append(A,Q,L2),
append([R|T],[O|TT],Q),
Z\=R,
F\=O.
You can use append/3 in this way to locate the first different elements.
first_different(L1,L2, R1,R2) :-
append(H, [E1|R1], L1),
append(H, [E2|R2], L2),
E1 \= E2.
H is the common part, R1,R2 are the 'remainders'. This code is more in line with your second comment above.
Now you must apply two times this helper predicate, and the second time also 'remainders' must be equal, or one of them must be empty. That is
friendly(L1,L2) :-
first_different(L1,L2,R1,R2),
first_different(R1,R2,T1,T2),
once((T1=T2;T1=[];T2=[])).
Alternatively, using some builtin can be rewarding. This should work
friendly(L1,L2) :- findall(_,(nth1(I,L1,E1),nth1(I,L2,E2),E1\=E2),[_,_]).
Wouldn't it be better to do something like the following?
diff([], [], []).
diff([], K, K).
diff(L, [], L).
diff([H | TL], [H | TK], D) :- diff(TL, TK, D),!.
diff([HL | TL], [HK | TK], [HL, HK | D]) :- diff(TL, TK, D),!.
friendly(K, L) :- diff(K, L, D), length(D, Length), Length < 3.
But your problem really is underspecified. For example my program really cares about order so [a,x,b] and [a,b] are not friendly by my definition.
I'm still a little uncertain about the total definition of "friendly" list, but I think this might answer it:
friendly(A, B) :-
friendly(A, B, 2).
friendly([H|TA], [H|TB], C) :-
C > 0,
friendly(TA, TB, C).
friendly([HA|TA], [HB|TB], C) :-
HA \= HB,
C > 0,
C1 is C-1,
friendly(TA, TB, C1).
friendly(A, [], C) :-
length(A, L),
L =< C.
friendly([], B, C) :-
length(B, L),
L =< C.
friendly(A, A, 0).
I'm assuming that the definition of friendly means the lists are in "lock step" outside of the maximum of two differences.
Does order matter? Are the lists sets (each element is unique) or bags (duplicates allowed)?
Assuming that
Order doesn't matter ([1,2,3] and [1,3,2]) are treated as identical), and
Duplicates don't matter ([1,2,3,1] and [1,2,3]) are treated as identical
Something like this might be along the lines of what you're looking for:
friendly(Xs,Ys) :-
set_of(
E ,
(
( member(E,Xs) ,
not( member(E,Ys) )
)
;
(
member(E,Ys) ,
not( member(E,Xs) )
) ,
Zs
) ,
length( Zs , L ) ,
L =< 2
.
Find the set of all elements of each list that aren't in the other and succeed if the resulting list is of length 0, 1 or 2.
I came up with something similar to others.. I just keep a list of '_' items (final param of diff_list) - one for each difference, be that a value difference at the same index, or a difference in the length, then finally in friendly/2, check that has 2 items.
% recursion base
diff_list([], [], []).
% the head of both lists are the same, don't add to diff list
diff_list([HK|TK], [HK|TL], Diff) :-
diff_list(TK, TL, Diff).
% the above rule failed, so must be a difference, add a '_'
diff_list([_|TK], [_|TL], [_|Diff]) :-
diff_list(TK, TL, Diff).
% 1st list is empty, but the 2nd isn't. That's a diff again.
diff_list([], [_|TL], [_|Diff]) :-
diff_list([], TL, Diff).
% 2nd list is empty, but the 1st isn't. Another diff.
diff_list([_|TK], [], [_|Diff]) :-
diff_list(TK, [], Diff).
% friendly is true if the diff list length unifies with 2 item length list [_,_]
friendly(K, L) :-
diff_list(K, L, [_,_]).
I am completely new to Prolog and trying some exercises. One of them is:
Write a predicate set(InList,OutList)
which takes as input an arbitrary
list, and returns a list in which each
element of the input list appears only
once.
Here is my solution:
member(X,[X|_]).
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :-
not(member(H,T)),
set(T,Out).
set([H|T],Out) :-
member(H,T),
set(T,Out).
I'm not allowed to use any of built-in predicates (It would be better even do not use not/1). The problem is, that set/2 gives multiple same solutions. The more repetitions in the input list, the more solutions will result. What am I doing wrong? Thanks in advance.
You are getting multiple solutions due to Prolog's backtracking. Technically, each solution provided is correct, which is why it is being generated. If you want just one solution to be generated, you are going to have to stop backtracking at some point. This is what the Prolog cut is used for. You might find that reading up on that will help you with this problem.
Update: Right. Your member() predicate is evaluating as true in several different ways if the first variable is in multiple positions in the second variable.
I've used the name mymember() for this predicate, so as not to conflict with GNU Prolog's builtin member() predicate. My knowledge base now looks like this:
mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).
not(A) :- \+ call(A).
set([],[]).
set([H|T],[H|Out]) :-
not(mymember(H,T)),
set(T,Out).
set([H|T],Out) :-
mymember(H,T),
set(T,Out).
So, mymember(1, [1, 1, 1]). evaluates as true in three different ways:
| ?- mymember(1, [1, 1, 1]).
true ? a
true
true
no
If you want to have only one answer, you're going to have to use a cut. Changing the first definition of mymember() to this:
mymember(X,[X|_]) :- !.
Solves your problem.
Furthermore, you can avoid not() altogether, if you wish, by defining a notamember() predicate yourself. The choice is yours.
A simpler (and likely faster) solution is to use library predicate sort/2 which remove duplicates in O(n log n). Definitely works in Yap prolog and SWIPL
You are on the right track... Stay pure---it's easy!
Use reified equality predicates =/3 and dif/3 in combination with if_/3, as implemented in Prolog union for A U B U C:
=(X, Y, R) :- X == Y, !, R = true.
=(X, Y, R) :- ?=(X, Y), !, R = false. % syntactically different
=(X, Y, R) :- X \= Y, !, R = false. % semantically different
=(X, Y, R) :- R == true, !, X = Y.
=(X, X, true).
=(X, Y, false) :-
dif(X, Y).
% dif/3 is defined like (=)/3
dif(X, Y, R) :- X == Y, !, R = false.
dif(X, Y, R) :- ?=(X, Y), !, R = true. % syntactically different
dif(X, Y, R) :- X \= Y, !, R = true. % semantically different
dif(X, Y, R) :- R == true, !, X \= Y.
dif(X, Y, true) :- % succeed first!
dif(X, Y).
dif(X, X, false).
if_(C_1, Then_0, Else_0) :-
call(C_1, Truth),
functor(Truth,_,0), % safety check
( Truth == true -> Then_0 ; Truth == false, Else_0 ).
Based on these predicates we build a reified membership predicate list_item_isMember/3. It is semantically equivalent with memberd_truth/3 by #false. We rearrange the argument order so the list is the 1st argument. This enables first-argument indexing which prevents leaving useless choice-points behind as memberd_truth/3 would create.
list_item_isMember([],_,false).
list_item_isMember([X|Xs],E,Truth) :-
if_(E = X, Truth = true, list_item_isMember(Xs,E,Truth)).
list_set([],[]).
list_set([X|Xs],Ys) :-
if_(list_item_isMember(Xs,X), Ys = Ys0, Ys = [X|Ys0]),
list_set(Xs,Ys0).
A simple query shows that all redundant answers have been eliminated and that the goal succeeds without leaving any choice-points behind:
?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [4,3,2,1]. % succeeds deterministically
Edit 2015-04-23
I was inspired by #Ludwig's answer of set/2, which goes like this:
set([],[]).
set([H|T],[H|T1]) :- subtract(T,[H],T2), set(T2,T1).
SWI-Prolog's builtin predicate subtract/3 can be non-monotone, which may restrict its use. list_item_subtracted/3 is a monotone variant of it:
list_item_subtracted([],_,[]).
list_item_subtracted([A|As],E,Bs1) :-
if_(dif(A,E), Bs1 = [A|Bs], Bs = Bs1),
list_item_subtracted(As,E,Bs).
list_setB/2 is like set/2, but is based on list_item_subtracted/3---not subtract/3:
list_setB([],[]).
list_setB([X|Xs1],[X|Ys]) :-
list_item_subtracted(Xs1,X,Xs),
list_setB(Xs,Ys).
The following queries compare list_set/2 and list_setB/2:
?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1], Xs).
Xs = [4,3,2,1]. % succeeds deterministically
?- list_setB([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [1,2,3,4]. % succeeds deterministically
?- list_set(Xs,[a,b]).
Xs = [a,b]
; Xs = [a,b,b]
; Xs = [a,b,b,b]
... % does not terminate universally
?- list_setB(Xs,[a,b]).
Xs = [a,b]
; Xs = [a,b,b]
; Xs = [a,b,b,b]
... % does not terminate universally
I think that a better way to do this would be:
set([], []).
set([H|T], [H|T1]) :- subtract(T, [H], T2), set(T2, T1).
So, for example ?- set([1,4,1,1,3,4],S) give you as output:
S = [1, 4, 3]
Adding my answer to this old thread:
notmember(_,[]).
notmember(X,[H|T]):-X\=H,notmember(X,T).
set([],[]).
set([H|T],S):-set(T,S),member(H,S).
set([H|T],[H|S]):-set(T,S),not(member(H,S)).
The only virtue of this solution is that it uses only those predicates that have been introduced by the point where this exercise appears in the original text.
This works without cut, but it needs more lines and another argument.
If I change the [H2|T2] to S on line three, it will produce multiple results. I don't understand why.
setb([],[],_).
setb([H|T],[H|T2],A) :- not(member(H,A)),setb(T,T2,[H|A]).
setb([H|T],[H2|T2],A) :- member(H,A),setb(T,[H2|T2],A).
setb([H|T],[],A) :- member(H,A),setb(T,[],A).
set(L,S) :- setb(L,S,[]).
You just have to stop the backtracking of Prolog.
enter code here
member(X,[X|_]):- !.
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :-
not(member(H,T)),
!,
set(T,Out).
set([H|T],Out) :-
member(H,T),
set(T,Out).
Using the support function mymember of Tim, you can do this if the order of elements in the set isn't important:
mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).
mkset([],[]).
mkset([T|C], S) :- mymember(T,C),!, mkset(C,S).
mkset([T|C], S) :- mkset(C,Z), S=[T|Z].
So, for example ?- mkset([1,4,1,1,3,4],S) give you as output:
S = [1, 3, 4]
but, if you want a set with the elements ordered like in the list you can use:
mkset2([],[], _).
mkset2([T|C], S, D) :- mkset2(C,Z,[T|D]), ((mymember(T,D), S=Z,!) ; S=[T|Z]).
mkset(L, S) :- mkset2(L,S,[]).
This solution, with the same input of the previous example, give to you:
S = [1, 4, 3]
This time the elements are in the same order as they appear in the input list.
/* Remove duplicates from a list without accumulator */
our_member(A,[A|Rest]).
our_member(A, [_|Rest]):-
our_member(A, Rest).
remove_dup([],[]):-!.
remove_dup([X|Rest],L):-
our_member(X,Rest),!,
remove_dup(Rest,L).
remove_dup([X|Rest],[X|L]):-
remove_dup(Rest,L).