Related
I tried to output a list only contain the unique element. But the result is not perfect.
list_concatenation([],L,L).
list_concatenation([X1|L1],L2,[X1|L3]) :-
list_concatenation(L1,L2,L3).
list_member(X, [X|_]).
list_member(X, [_|TAIL]) :- list_member(X,TAIL),!.
toset([],_).
toset([X|TAIL],SET):-
list_member(X,SET),
toset(TAIL,SET).
toset([X|TAIL],SET):-
\+ list_member(X,SET),
list_concatenation([X],SET,NEWSET),
toset(TAIL,NEWSET).
For example:
?- toset([1,1,2,3],X).
the result should be 'X = [1, 2, 3]' but now, it is 'X = [1, 2, 3|_16998]'
You actually implement the toset/2 the wrong way. You actually use list_member/2 here to add an element to the list. Indeed, if you use a free variable, you get:
?- list_member(2, L).
L = [2|_3616] ;
L = [_3614, 2|_3622].
The list_member/2 itself, is not correctly implemented as well. The cut (!) here prevents to keep yielding values. You thus should remove the cut:
list_member(X, [X|_]).
list_member(X, [_|Tail]) :-
list_member(X,Tail).
This thus can yield all possible lists where 2 is a member:
?- list_member(2, L).
L = [2|_3412] ;
L = [_3410, 2|_3418] ;
L = [_3410, _3416, 2|_3424] ;
L = [_3410, _3416, _3422, 2|_3430] ;
...
But now the problem still remains: you should not use list_member/2 here to add elements to the list, you can use an accumulator here that keeps track of the elements already yielded, and then eventually return that accumulator, like:
toset(L, S) :-
toset(L, [], S).
toset([], A, S):-
reverse(A, S).
toset([H|T], A, L) :-
( member_list(H, A)
-> toset(T, [H|A], L)
; toset(T, A, L)
).
We thus keep prepending the values to the accumulator, and in the end, we reverse/2 [swi-doc] the accumulator, and return that as a result.
I'm trying to print out all possible shuffled variants of two lists in one list while preserving the order.
I need to write a predicate shuffle(L1, L2, L3) which shuffles L1 and L2 and puts the result into L3 while preserving the inner order of L1 and L2.
For example :
?- shuffle([a,b],[1,2],L).
L = [a,b,1,2] ;
L = [a,1,b,2] ;
L = [a,1,2,b] ;
L = [1,a,b,2] ;
L = [1,a,2,b] ;
L = [1,2,a,b]
What I have so far :
shuffle([],[],[]).
shuffle([X|Xs],[Y|Ys],[X,Y|Tail]) :-
shuffle(Xs,Ys,Tail).
shuffle([X|Xs],[Y|Ys],[Y,X|Tail]) :-
shuffle(Xs,Ys,Tail).
This results in :
| ?- shuffle([a,b],[1,2],L).
L = [a,1,b,2] ? ;
L = [a,1,2,b] ? ;
L = [1,a,b,2] ? ;
L = [1,a,2,b]
So I'm missing the cases of "simple append" of L1+L2 and L2+L1...
What is my predicate missing?
We can use dcg for its ease of writing:
shuffle([A|B],[C|D]) --> [A] , shuffle(B,[C|D]).
shuffle([A|B],[C|D]) --> [C] , shuffle([A|B],D).
shuffle(A,[]) --> A.
shuffle([],C) --> C.
shuffle( A, B, C) :- phrase( shuffle(A,B), C).
We either take first card from one non-empty deck or the other, but if one of them is empty we must use all the remaining cards in the non-empty deck at once.
Unfortunately this leaves one extra choice point at the end:
5 ?- shuffle([a,b],[1,2],C).
C = [a, b, 1, 2] ;
C = [a, 1, b, 2] ;
C = [a, 1, 2, b] ;
C = [1, a, b, 2] ;
C = [1, a, 2, b] ;
C = [1, 2, a, b] ;
false.
As for your approach the problem with it was that you tried to take care of two cards at once, and it got complicated. Going by smallest steps can be the easiest.
Here's how you can shuffle two lists while preserving the relative item order.
shuffle([], Xs, Xs).
shuffle([X|Xs], Ys, Zs) :-
shuffle_(Ys, X, Xs, Zs). % use auxiliary predicate shuffle_/4
shuffle_([], X, Xs, [X|Xs]). % do indexing on both lists
shuffle_([Y|Ys], X, Xs, [X|Zs]) :-
shuffle_(Xs, Y, Ys, Zs).
shuffle_([Y|Ys], X, Xs, [Y|Zs]) :-
shuffle_(Ys, X, Xs, Zs).
Sample query using SWI-Prolog:
?- shuffle([a,b], [1,2], Xs).
Xs = [a,1,b,2]
; Xs = [a,1,2,b]
; Xs = [a,b,1,2]
; Xs = [1,a,2,b]
; Xs = [1,a,b,2]
; Xs = [1,2,a,b]. % no useless choice point at the end
#repeat's answer is more elegant and efficient, but, as an alternative:
The unwanted choice-point can be removed using a reusable empty_list_first predicate:
shuffle([A|B], [C|D]) --> [A],
shuffle(B, [C|D]).
shuffle([A|B], [C|D]) --> [C],
{ empty_list_first([A|B], D, A1, D1) },
shuffle(A1, D1).
% Rewritten to prevent needing https://www.swi-prolog.org/pldoc/man?section=basics
%shuffle([], C) --> remainder(C).
shuffle([], C, C, []).
shuffle(A, B, C) :-
empty_list_first(A, B, A1, B1),
phrase(shuffle(A1, B1), C).
empty_list_first([], L2, [], L2).
empty_list_first([H|T], L2, EL1, EL2) :-
empty_list_first_(L2, [H|T], EL1, EL2).
empty_list_first_([], L1, [], L1).
% If neither are empty, keep original order
empty_list_first_([H|T], L1, L1, [H|T]).
Result in swi-prolog:
?- shuffle([a,b], [1,2], C).
C = [a,b,1,2] ;
C = [a,1,b,2] ;
C = [a,1,2,b] ;
C = [1,a,b,2] ;
C = [1,a,2,b] ;
C = [1,2,a,b].
My answer is posted long time after original question but hoping this might prove useful to someone some day. I've taken a different approach to this, might be on the longer side but it works... :)
Since one of the requirements at the class I'm taking to not exceed material learned, some items such as delete and concatenate have been created here as well.
del(X,[X|Xs],Xs).
del(X,[Y|Ys],[Y|Zs]):-
del(X,Ys,Zs).
permutation([],[]).
permutation(Xs,[Z|Zs]):-
del(Z,Xs,Ys),
permutation(Ys,Zs).
conc([],L,L).
conc([X|L1],L2,[X|L3]):-
conc(L1,L2,L3).
is_in_order([],_).
is_in_order([_],_).
is_in_order(Sublist1, Sublist2, Superlist) :-
remove_elements(Superlist, Sublist1, SuperSubList),
list_equal(Sublist2, SuperSubList).
list_equal([], []).
list_equal([X|Xs],[X|Ys]) :-
list_equal(Xs, Ys).
% Remove L1 from L2 and return the resulting list
remove_elements(L, [H|T], R) :-
delete(L, H, R1),
remove_elements(R1, T, R).
remove_elements(L, [], L).
/*Shuffle first creates a concatenated list from both L1 & L2
* It then create permutation for all possible combinations of L1 & L2
* Once done, it scrubs the new lists to filter out the ones that do not
* maintain the original order of L1 & L2
* The result is only the permutations that fullfills the order condition
*/
shuffle(L1,L2,L):-
conc(L1,L2,L3),
permutation(L3, L),
is_in_order(L1, L2, L),
is_in_order(L2, L1, L).
I'm trying to rewrite code from Haskell to Prolog.
count :: Eq a => a -> [a] -> Int
count x = length . filter (x==)
f :: [Integer] -> [Integer]
f [] = []
f list = filter (\x -> count x list == 1) list
This code return list that contains elements that appears only once in the list.
So if I have list [1,1,2,2,3,4,4,5] this function returns [3,5]
I tried to find filter construction in Prolog but seems there no such thing. How can I make similar function in Prolog ?
To the existing answers, I would like to add an answer that is quite general in the sense that you can use it in multiple directions.
Building block: list_element_number/3
I start with the following predicate, defining a relation between:
a list Ls0
an element E
the number N of occurrences of E in Ls0
Here it is:
list_element_number(Ls0, E, N) :-
tfilter(=(E), Ls0, Ls),
length(Ls, N).
This solution uses tfilter/3 from library(reif). The predicate subsumes the function count you have posted. The main benefit of this predicate over the function is that the predicate can be used not only in those cases that even Haskell can do easily, such as:
?- list_element_number([a,b,c], a, N).
N = 1.
No, we can use it also in other directions, such as:
?- list_element_number([a,b,c], X, 1).
X = a ;
X = b ;
X = c ;
false.
Or even:
?- list_element_number([a,b,E], X, 2).
E = X, X = a ;
E = X, X = b ;
false.
Or even:
?- list_element_number([A,B,C], X, 3).
A = B, B = C, C = X ;
false.
And even in the most general case, in which all arguments are fresh variables:
?- list_element_number(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 ;
Ls = [E, E],
N = 2 ;
Ls = [E, E, E],
N = 3 .
We can fairly enumerate all answers like this:
?- length(Ls, _), list_element_number(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 ;
Ls = [_160],
N = 0,
dif(E, _160) ;
Ls = [E, E],
N = 2 .
Main predicate: list_singletons/2
Using this building block, we can define list_singletons/2 as follows:
list_singletons(Ls, Singles) :-
tfilter(count_one(Ls), Ls, Singles).
count_one(Ls, E, T) :-
list_element_number(Ls, E, Num),
cond_t(Num=1, true, T).
This uses cond_t/3 and (again) tfilter/3 from library(reif).
Sample queries
Here are a few sample queries. First, the test case you have posted:
?- list_singletons([1,1,2,2,3,4,4,5], Singles).
Singles = [3, 5].
It works as desired.
Now a case involving variables:
?- list_singletons([A,B], Singles).
A = B,
Singles = [] ;
Singles = [A, B],
dif(A, B).
On backtracking, all possibilities are generated: Either A = B holds, and in that case, there is no element that occurs only once. Or A is different from B, and in that case both A and B occur exactly once.
As a special case of the above query, we can post:
?- list_singletons([A,A], Singles).
Singles = [].
And as a generalization, we can post:
?- length(Ls, _), list_singletons(Ls, Singles).
Ls = Singles, Singles = [] ;
Ls = Singles, Singles = [_7216] ;
Ls = [_7216, _7216],
Singles = [] ;
Ls = Singles, Singles = [_7828, _7834],
dif(_7828, _7834) ;
Ls = [_7216, _7216, _7216],
Singles = [] ;
Ls = [_7910, _7910, _7922],
Singles = [_7922],
dif(_7910, _7922) .
Enjoy the generality of this relation, obtained via logical-purity.
A more simple version :
filter_list(L,OutList):-findall(X, (select(X,L, L1),\+member(X, L1)) , OutList).
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
Without findall, you can try
filter_list(In, Out) :- filter_list(In, _, Out).
filter_list([], [], []).
filter_list([H|T], L1, L2) :-
filter_list(T, LL1, LL2),
( member(H, LL1)
-> L1 = LL1, L2 = LL2
; (select(H, LL2, L2)
-> L1 = [H|LL1]
; L1 = LL1, L2 = [H|LL2])).
without counting...
filter_uniques([],[]).
filter_uniques([H|T],F) :-
delete(T,H,D),
( D=T -> F=[H|R],S=T ; F=R,S=D ),
filter_uniques(S,R).
a more direct rewrite of your code, with library(yall) support for inlining of the filter predicate (the first argument to include/3)
filt_uniq(L,F) :-
include({L}/[E]>>aggregate(count,member(E,L),1),L,F).
A simple version:
(see the comment by #false below, the version with findall/3 has some inconsistency problems in more complex queries but second version looks ok however it is definitely not so efficient ).
filter_list(L,OutList):-findall(X, (member(X,L),count(X,L,N),N=:=1) , OutList).
count(_,[],0).
count(X,[X|T],N):-count(X,T,N1),N is N1+1.
count(X,[X1|T],N):-dif(X,X1),count(X,T,N).
The predicate filter_list/2 uses findall/3 and simply states find all X that belong to the list L and count returns 1 and store them in OutList.
Example:
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
You could write filter_list/2 without using findall/3 like:
filter_list(L,OutList):- filter_list(L,OutList,L).
filter_list([],[],_).
filter_list([H|T],[H|T1],L):-count(H,L,N), N=:=1, filter_list(T,T1,L).
filter_list([H|T],T1,L):-count(H,L,N), N > 1, filter_list(T,T1,L).
I found a similar post but it didn't work for me. So please don't try to redirect me to other links.
This is the result that I want:
removeadj([a,a,a,b,c,c,a,a],[a,b,c,a]).
>True.
I tried this code:
concatener([],R,R).
concatener([X|Y],L,R):-concatener(Y,L,R1),R=[X|R1].
removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),concatener([],R1,R).
removeadj([X|Y],R):- removeadj(Y,R1),concatener(X,R1,R).
When I try a list with one element duplicated many times, it works:
removeadj([a,a,a,a,a],[a]).
> True
But when I use different elements, it doesn't work:
removeadj([a,a,a,b],[a,b]).
> False.
I do not see where the problem is, so I can't fix it. Please I need your help.
Relational names
The first is to reconsider the name of your relation. Currently, it suggests that someone has to do something. removeadj that's a command. Quite an adequate name in a programming language where commands are the ruling metaphor. But not in Prolog.
In Prolog, we have relations. A name that reflects this relationness is often very helpful. Why not list_list/2? After all, your relation is about two lists! OK, maybe that name was a bit too general. What about list__list_without_adjacent_elements/2? Lengthy, but relational. Maybe we shorten that to: list_noadjs/2. Note the s at the end: That means: it's a plural which means it's a list.
Observe properties
Before thinking about "doing" this or that. Rather muse about concrete examples - preferably ground examples, as you have given them. And about other properties. One observation is that all elements of the second list will be there in the first. In fact not only that. But they will also occur in the same order. Let me try to formulate that. Of course, that observation is not sufficient to write an entire predicate. But here comes the cool thing in Prolog: We do not need to implement everything. We can start with gross generalizations that contain all what we want plus some more.
Start with a too general definition.
To show you the most extreme, let's try:
list_noadjs(_Xs, _Ys).
This is the mother of all binary relations! That definition always succeeds, no matter what. Evidently, we will have to specialize it. Say, by looking at the second argument which is a list:
list_noadjs(_Xs, []).
list_noadjs(_Xs, [Y|Ys]) :-
list_noadjs(_, Ys).
If the list is [] so will be the original one. And both start with the same element!
list_noadjs(Xs, []) :-
Xs = [].
list_noadjs(Xs, [Y|Ys]) :-
Xs = [Y|_],
list_noadjs(_, Ys).
or more compactly:
list_noadjs([], []).
list_noadjs([Y|_Xs], [Y|Ys]) :-
list_noadjs(_, Ys).
Now, the first list contains elements of the second list. And in between something else:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
list_(Xs0,Xs1),
list_noadjs(Xs1, Ys).
list_(Xs,Xs).
list_([_|Xs0],Xs) :-
list_(Xs0,Xs).
Is this already our relation? Let's give it a try:
?- list_noadjs("aaab",Ys).
Ys = "aaab"
; Ys = "aaa"
; Ys = "aab"
; Ys = "aa"
; Ys = "aab"
; Ys = "aa"
; Ys = "ab" % <===== * RRRIGHT !!!!***
; Ys = "a"
; false.
(Btw. I am using library(double_quotes) to make answers more readable.)
So we do have the expected solution. Alas, there are many incorrect solutions, too! We will have to continue to specialize this program:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
eq_list_(Y, Xs0,Xs1),
list_noadjs(Xs1, Ys).
eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
eq_list_(Y, Xs0,Xs).
Now this is much better, but still not perfect:
?- list_noadjs("aaab",Ys).
Ys = "aaab"
; Ys = "aab"
; Ys = "aab"
; Ys = "ab" % !!! Right
; false.
We have to further specialize the program: After a sequence of identical elements, there must be something else:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
eq_list_(Y, Xs0,Xs1),
nohead(Xs1, Y),
list_noadjs(Xs1, Ys).
eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
eq_list_(Y, Xs0,Xs).
nohead([], _X).
nohead([X|_], Y) :-
dif(X, Y).
So that's our relation.
Enjoy the relation!
Seriously. Don't just use the test cases you had. You have now a relation! That's not a function in disguise, it is truly more than that. Try it out! Ask completely unusual things, like:
?- list_noadjs(Xs,"abc").
Xs = "abc"
; Xs = "abcc"
; Xs = "abccc"
; Xs = "abcccc"
; ... .
So here we ask: Which lists correspond to "abc"? Note that only c is repeated! All the other solutions are hidden behind this wall of infinity. But we can play a little trick to get them:
?- length(Xs,N), list_noadjs(Xs,"abc").
Xs = "abc", N = 3
; Xs = "abcc", N = 4
; Xs = "abbc", N = 4
; Xs = "aabc", N = 4
; Xs = "abccc", N = 5
; Xs = "abbcc", N = 5
; Xs = "abbbc", N = 5
; Xs = "aabcc", N = 5
; Xs = "aabbc", N = 5
; Xs = "aaabc", N = 5
; Xs = "abcccc", N = 6
; ... .
Don't shy away from non-termation.
We have already seen it: Very often, we get infinitely many solutions. And (I must admit) it's even worse: Sometimes, our relations do not even terminate.
Here is one such example. Say, is there a way that the list in the second argument contains duplicates? Or that the following holds:
?- list_noadjs(Xs,[X,X]).
loops.
Prolog answers: mumble, mumble, lemme see...
To master Prolog, you will have to understand this in detail. But for the moment, there is often a good way out:
Specialize queries to get termination
So instead of asking: Is there any term that may correspond to [X,X] we may ask: Is there a list of size 5 (or any other finite size). Now Prolog denies this:
?- Xs = [_,_,_,_,_], list_noadjs(Xs,[X,X]).
false.
That's not the universal answer you wanted. But it is better than nothing.
Sometimes all these queries are too much for you. Let Prolog do the thinking for you by:
Enumerating all solutions
Often, this is very simple. Start with the most general query. The big advantage here is that no thinking is required on your side at all. And still you look like a pro:
?- list_noadjs(Xs,Ys).
Xs = [], Ys = []
; Xs = [_A], Ys = [_A]
; Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_B,_C], Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B) ?
; Xs = [_A,_B,_C,_D], Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; Xs = [_A,_B,_C,_D,_E], Ys = [_A,_B,_C,_D,_E], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; ... .
What we got here are so called answers. A single answer may contain infinitely many solutions. Think of this: You are looking at infinity! Some conditions (called constraints) must hold, like dif/2. But that's it.
The third answer was:
Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A)
So Xs and Ys are the same list with two distinct elements. So this answer implies Xs = "ab", Ys = "ab" but also Xs = [1,2], Ys = [1,2] and many, many more.
Even better, enumerate all answers in a systematic ("fair") manner:
?- length(Xs, N), list_noadjs(Xs,Ys).
Xs = [], N = 0, Ys = []
; Xs = [_A], N = 1, Ys = [_A]
; Xs = [_A,_B], N = 2, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A], N = 2, Ys = [_A]
; Xs = [_A,_B,_C], N = 3, Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B)
; Xs = [_A,_B,_B], N = 3, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A,_B], N = 3, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A,_A], N = 3, Ys = [_A]
; Xs = [_A,_B,_C,_D], N = 4, Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; ... .
These are all solutions up to length 3. There are no other! We know this for sure because the last answer is already of size 4. So all solutions below are here already!
Often looking at such answers is very helpful. For example, it permits you to rapidly detect errors (like in the other answer that was given previously). So, don't tell nobody about that trick.
The last clause in the predicate still has an issue:
removeadj([X|Y], [X|R1]):- removeadj(Y, R1).
In the event that the element following X is also X, X is still carried in the head of the second argument. This clause must check if the following element is different before allowing it in the second argument:
removeadj([X,Y|L], [X|R]) :-
dif(X,Y),
removeadj([Y|L], R).
Here, Y is at the head of the second list only if it's different from X.
So the whole solution looks like:
removeadj([], []).
removeadj([X], [X]).
removeadj([X,X|T], R) :- % drop an X if next element is X
removeadj([X|T], R).
removeadj([X,Y|T], [X|R]) :- % carry X along if next element different
dif(X,Y),
removeadj([Y|T], R).
removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),R=R1.
removeadj([X|Y],[X|R1]):- removeadj(Y,R1).
The aim is to implement the predicate noDupl/2.
The first argument of this predicate is the list to analyze and second argument is the list of numbers which are no duplicate.
I could not understand code below and when I compiled it, it gave an error message that contained is undefined procedure, however as a hint it is written that we can use as predefined predicate contained and notContained. I think I need to define contained and notContained.
noDupl(XS, Res):-
help( XS, [],Res).
help([],_,[]).
help([X|XS],Seen,[X|Res]):-
notContained(X,XS),
notContained(X,Seen),
help(XS, [X|Seen], Res).
help([X|XS],Seen,Res):-
contained(X,Seen),
help(XS, Seen, Res).
help([X|XS],Seen,Res):-
contained(X,XS),
help(XS, [X|Seen], Res).
Could someone please explain me the problem.
The missing definitions might be:
contained(X,[X|_]).
contained(X,[E|Es]) :-
dif(X, E),
contained(X, Es).
notContained(_X, []).
notContained(X, [E|Es]) :-
dif(X, E),
notContained(X, Es).
(I like to call these relations rather memberd/2 and non_member/2.)
The definition you gave extends the relation with an extra argument for the elements considered so far.
To understand the meaning of each clause, read each right-to-left in the direction of the arrow (the :- is a 1970's ASCII-fication of ←). Let's take the first rule:
Provided, that X is not an element of XS, and
provided, that X is not an element of Seen, and
provided, that help(X, [X|Seen], Res) is true,
then also help([X|XS],Seen,[X|Res]) is true.
In other words, if X is neither in the list of visited elements Seen nor in the elements yet to be visited XS, then it does not possess a duplicate.
What is a bit difficult to understand is whether or not the clauses you gave are mutually exclusive - this is, strictly speaking, not your concern, as long as you are only interested in declarative properties, but it is a good idea to avoid such redundancies.
Here is a case, where such redundancy shows:
?- noDupl([a,a,a],U).
U = []
; U = []
; false.
Ideally, the system would give one determinate answer:
?- noDupl([a,a,a], U).
U = [].
Personally, I do not like a lot to split things into too many cases. Essentially, we could have two: it is a duplicate, and it is none.
It is possible to provide a definition that is correct and still fully determinate for the cases where determinism is possible - such as when the first argument is "sufficiently instantiated" (which includes a ground list). Let's see if there are some answers into that direction.
I've annotated your code for you:
noDupl( XS , Res ) :- % Res is the [unique] set of element from the bag XS
help( XS, [],Res) % if invoking the helper succeeds.
. %
help( [] , _ , [] ) . % the empty list is unique.
help( [X|XS] , Seen , [X|Res] ) :- % A non-empty list is unique, if...
notContained(X,XS), % - its head (X) is not contained in its tail (XS), and
notContained(X,Seen), % - X has not already been seen, and
help(XS, [X|Seen], Res). % - the remainder of the list is unique.
help( [X|XS] , Seen , Res ) :- % otherwise...
contained(X,Seen) , % - if X has been seen,
help(XS, Seen, Res). % - we discard it and recurse down on the tail.
help([X|XS],Seen,Res):- % otherwise...
contained(X,XS), % - if X is in the tail of the source list,
help(XS, [X|Seen], Res). % - we discard it (but add it to 'seen').
Your contained/2 and notContained/2` predicates might be defined as this:
contained( X , [X|_] ) :- ! .
contained( X , [Y|Ys] ) :- X \= Y , contained( X , Ys ) .
not_contained( _ , [] ) .
not_contained( X , [Y|Ys] ) :- X \= Y , not_contained(X,Ys) .
Now, I may be missing something in your code, but there's an awful lot of redundancy in it. You could simply write something like this (using the built-ins member/2 and reverse/2):
no_dupes( List , Unique ) :- no_dupes( Bag , [] , Set ) .
no_dupes( [] , V , S ) . % if we've exhausted the bag, the list of visited items is our set (in reverse order of the source)
reverse(V,S) % - reverset it
. % - to put our set in source order
no_dupes( [X|Xs] , V , S ) :- % otherwise ...
( member(X,V) -> % - if X is already in the set,
V1 = V % - then we discard X
; V1 = [X|V] % - else we add X to the set
) , % And...
no_dupes( Xs , V1 , S ) % we recurse down on the remainder
. % Easy!
Can this be done in a pure and efficient way?
Yes, by using
tpartition/4 and (=)/3 like so:
dups_gone([] ,[]).
dups_gone([X|Xs],Zs0) :-
tpartition(=(X),Xs,Ts,Fs),
if_(Ts=[], Zs0=[X|Zs], Zs0=Zs),
dups_gone(Fs,Zs).
Some sample ground queries (all of which succeed deterministically):
?- dups_gone([a,a,a],Xs).
Xs = [].
?- dups_gone([a,b,c],Xs).
Xs = [a, b, c].
?- dups_gone([a,b,c,b],Xs).
Xs = [a, c].
?- dups_gone([a,b,c,b,a],Xs).
Xs = [c].
?- dups_gone([a,b,c,b,a,a,a],Xs).
Xs = [c].
?- dups_gone([a,b,c,b,a,a,a,c],Xs).
Xs = [].
This also works with more general queries. Consider:
?- length(Xs,N), dups_gone(Xs,Zs).
N = 0, Xs = [], Zs = []
; N = 1, Xs = [_A], Zs = [_A]
; N = 2, Xs = [_A,_A], Zs = []
; N = 2, Xs = [_A,_B], Zs = [_A,_B], dif(_A,_B)
; N = 3, Xs = [_A,_A,_A], Zs = []
; N = 3, Xs = [_A,_A,_B], Zs = [_B], dif(_A,_B)
; N = 3, Xs = [_A,_B,_A], Zs = [_B], dif(_A,_B)
; N = 3, Xs = [_B,_A,_A], Zs = [_B], dif(_A,_B), dif(_A,_B)
; N = 3, Xs = [_A,_B,_C], Zs = [_A,_B,_C], dif(_A,_B), dif(_A,_C), dif(_B,_C)
; N = 4, Xs = [_A,_A,_A,_A], Zs = []
...