问题
I'm having some trouble removing values from a list in prolog. I have a list of colors and I want to add a list of colors to it and keep all the values that have no duplicate and remove the rest.
[green, red, blue, purple, yellow, brown, orange, black, purple]
so purple appears twice in this list and I want to remove both of them. This is the list I want to be returned.
[green, red, blue, yellow, brown, orange, black]
I currently have this to remove all the duplicates but I can't get both purples out.
mymember(X,[H|_]) :- X==H,!.
mymember(X,[_|T]) :- mymember(X,T).
not(A) :- \+ call(A).
set([],[]).
set([Head|Tail],[Head|Out]) :-
not(mymember(Head,Tail)),
set(Tail, Out).
set([Head|Tail],Out) :-
mymember(Head,Tail),
set(Tail, Out).
this is the result I get now:
[green, red, blue, yellow, brown, orange, black, purple]
回答1:
The simple way...a one-liner:
singletons(Xs,Zs) :-
findall( X , ( append(P,[X|S],Xs), \+member(X,P), \+member(X,S) ) , Zs )
.
回答2:
Stay pure by using tfilter/3 in combination with list_uniqmember_t/3!
list_uniqs(Es, Us) :-
tfilter(list_uniqmember_t(Es), Es, Us).
The sample query given by the OP has the expected result:
?- list_uniqs([green,red,blue,purple,yellow,brown,orange,black,purple], Xs).
Xs = [green,red,blue,yellow,brown,orange,black]. % succeeds deterministically
Do we get logically sound answers for more general queries, too?
?- list_uniqs([A,B,A], []). A=B ; false. ?- list_uniqs([A,B,A], [_]). dif(A,B). ?- list_uniqs([A,B,A], [_,_]). false. ?- list_uniqs([A,B,A], Xs). Xs = [] , A=B ; Xs = [B], dif(A,B).
Yes! How about something a little more general?
?- list_uniqs([A,B,C],Xs). Xs = [] , A=B , B=C ; Xs = [C] , A=B , dif(B,C) ; Xs = [B] , A=C , dif(B,C) ; Xs = [A] , dif(A,C), B=C ; Xs = [A,B,C], dif(A,B), dif(A,C), dif(B,C).
It works!
回答3:
I think you're on the right track. Here's an approach using the -> ; construct and taking advantage of the delete/3 predicate, which removes ALL duplicates:
remdup([], _, []).
remdup([H|T], X, R) :-
( H == X
-> ( member(X, T)
-> delete(T, X, R) % only delete if it's in the list more than once
; R = [H|R1],
remdup(T, X, R1)
)
; R = [H|R1],
remdup(T, X, R1)
).
Another solution, using select/3
as well as delete/3
:
remdup(L, X, R) :-
(select(X, L, L1), select(X, L1, L2))
-> delete(L2, X, R)
; L = R.
select/3
extracts one instance of an element from a list. If it cannot find the element, it fails. So in the above, we delete all the instances if we're able to find at least two of them.
A solution which does the whole list (minus the middle argument that specifies the single item to remove):
remdup([], []).
remdup([H|T], R) :-
( select(H, T, T1)
-> delete(T1, H, R1),
remdup(R1, R)
; R = [H|R1],
remdup(T, R1)
).
回答4:
I fixed it by doing this:
my_delete(Res, [], Res).
my_delete(Colorslist, [Head|Tail], R) :-
my_delete_worker(Colorslist, Head, Result),
my_delete(Result, Tail, R).
my_delete_worker([], _, []).
my_delete_worker([X|T], X, R) :-
my_delete_worker(T, X, R).
my_delete_worker([H|T], X, [H|R]) :-
X \= H,
my_delete_worker(T, X, R).
I forgot to put the result of the first color into the colorslist for the second color. When it arrives at the base case I unify my colorslist with the result. Thanks for your help lurker!
回答5:
simpler (and impure :-)
singletons(Xs, Zs) :- findall(X, (select(X,Xs,Ys), \+memberchk(X,Ys)), Zs).
来源:https://stackoverflow.com/questions/33259691/remove-both-the-value-and-all-duplicates-of-that-value-in-a-list-in-prolog