The builtin predicate setof/3 can be used to create an ordered list without duplicates.
Can I also use it to test whether a list represents a set with no duplicates? Like this?
no_duplicates(L) :- setof(_,_,L).
You cannot use it in exactly the way you describe. BUT, You can use setof to determine if a list is a set if you ask it to iterate through your list for members, then check the set against the original. If they have the same length, then all elements were unique.
no_duplicates(L) :-
setof(X, member(X, L), Set),
length(Set, Len),
length(L, Len).
You can't. Your arguments are not sufficiently instantiated.
If you are using SWI-Prolog you can use the predicate
is_set/1 in the lists module.
Related
I am attempting to remove unique elements from a list in Prolog.
Output should look something like:
?- rem_Uniq([3,3,1,7,a,c,c],D).
D = [3, c].
Here is my current code.
rem_Uniq(L1,L2).
rem_Uniq([L1|RL1], [L1|D]) :-
member(L1,RL1),
rem_Uniq(RL1,D).
rem_Uniq([L1|RL1], D) :-
remove(L1[L1|RL1], O),
rem_Uniq(O, D).
Currently it just returns true no matter what I do (whether I enter I list containing unique variables or not).
Anyone have any ideas or suggestions on what I am doing wrong?
D is the set of elements of the list which appears only one time.
In Prolog "an element which appears only one time in a list" can be translate by
select(X, L, L_X),
\+member(X, L_X)
In Prolog exist predicates that collect element with a certain property setof/3 and bagof/3.
bagof collect all the elements, setof keeps only one element.
So you can write
rem_uniq(In, Out) :-
setof(X, In_X^(select(X, In, In_X),\+member(X, In_X)), Out).
[EDIT]
Now we want only elements that are duplicated in a list. If I remove one of these elements of the list, it will remain other elements of the same value in the list so it can be translated in Prolog by
select(X, In, In_X),
member(X, In_X)
(we say that select(X, In, In_X),member(X, In_X) succeed).
Now the code can be written
rem_uniq(In, Out) :-
setof(X, In_X^(select(X, In, In_X),member(X, In_X)), Out).
For example
?- rem_uniq([3,3,1,7,a,c,c],D).
D = [3,c].
Note that setof will fail if there no elements available
?- rem_uniq([3,1,7,a,c],D).
false.
Well, your first problem is your first clause:
rem_Uniq(L1,L2).
This literally says "Any two things are rem_Uniq to each other." This is what's giving rise to always getting true with no unifications. You probably meant this:
rem_Uniq([], []).
Your second problem is that this is not valid syntax:
remove(L1[L1|RL1], O),
Specifically, L1[L1|RL1], I am unclear what you meant there. I think you meant this delete(L1, [L1|RL1], O).
Now, algorithmically, I think you're a little confused. In clause #2, you prepend L1 to D in the result, which is to say, after knowing that L1 is present in RL1 and using the recursive call to remove it from D. But then in clause #3, you just remove it from [L1|RL1] to make O, which you then remove uniques from.
Each clause of a recursive predicate should represent a case you have to worry about. I don't really see what these clauses mean. The first one should be, in case where the list is empty. The second one should be the case where the list is not empty. What you seem to be trying to do here is something like, in the case where the list is not empty and contains the head element, and the case where it is not empty and does not contain the head element, but the distinction between having or not having that element is (or ought to be) meaningless to your library routine. In other words, delete/3 in one non-empty recursive case should be totally sufficient for this problem:
rem_uniq([], []).
rem_uniq([X|Xs], [X|UniqueXs]) :-
delete(X, Xs, XsWithoutX),
rem_uniq(XsWithoutX, UniqueXs).
So, I think you have a little confusion about when and why you should have multiple clauses, and I think your choice of variable names may have made life harder on yourself. But that's just my guess.
Hope this helps!
I want to write a rule to exhaust the database and fill a list with some items but the prolog interpreter always returns with: Out of global stack.
fill(SomeParams, List) :- append(List, [NewItem], List), fail.
Is there a way to extend a list without doing unification?
Your append can't work because it states that List is the result of appending [NewItem]to List, so List is supposed to be two things at once. Remember, variables in Prolog are logical variables that can never be changed, once they are bound.
You can use findall/3, as CapelliC said, or if you want more control, you can do something like this:
%% some facts
fact(1).
fact(2).
fact(3).
fill(List, NewList) :-
fact(X),
not(member(X, List)), !,
fill([X|List], NewList).
fill(List, List).
%%
fill(NewList) :- fill([], NewList).
Then
try ?- fill(L).
When you call member(Item, List) with an uninstanciated list, Prolog unifies and returns a list containing item. I want a rule that returns true/false and does not try to unify. Is there such a rule?
Quick answer: Use \+ \+ member(Item, List).
Please note that such tests often do not make a lot of sense when your programs represent logical relations.
You stated that member(Item, List) "returns a list". Well, that is not entirely true. List is unified with partial lists, that is List = [Item|_Rest] ; List = [_,Item|_Rest] ; ... with _Rest being an uninstantiated variable. That is, the goal member(Item, List) does not guarantee that (upon success) List is a list. Here is a counterexample: member(Item, List), List = [_|nonlist]
I would use a guard, like
is_member(E, L) :- nonvar(L), memberchk(E, L).
memberchk/2 it's a deterministic version of member/2, to be used to find if the list contains at least 1 occurrence of element. Cannot act as a generator, but it's more efficient. The guard is required anyway.
Suppose I have a list of lists
L= [[1,2,3], [3,2,1],[2,1,2],[3,1,2], [1,2,2]].
as you can see, [1,2,3],[3,2,1] and [3,1,2] are permutations of each other.
[2,1,2] and [1,2,2] are also permutations of each other.
My goal is to remove all permutations of elements in the list.
the result list should be:
L'=[[1,2,3],[2,1,2]].
My idea so far is use member(X,L) to locate an element in the list, then use permutation(X,Xperm) to get a permutation of X, then check if Xperm
is in L , and if so ,delete it.
However the result turns out not to be what I wanted.
Could anyone help me?
One way to eliminate the duplicates is to use the standard recursive process for removing duplicates, but rather than checking equality directly through unification, change the code to try unifying sorted lists.
/* This is the regular duplicate elimination
that sorts the head element before checking for duplicates
*/
remove_dups([],[]).
remove_dups([H|T], TT) :- msort(H,SH), contains_dup(SH,T), remove_dups(T, TT).
remove_dups([H|T], [H|TT]) :- msort(H,SH), \+ contains_dup(SH,T), remove_dups(T, TT).
/* This duplicate checker routine sorts the lists before trying to unify them */
contains_dup(_, []) :- fail.
contains_dup(SH, [H|_]) :- msort(H, SH).
contains_dup(SH, [_|T]) :- contains_dup(SH, T).
The code uses SWI's msort/2 predicate.
Here is a demo on ideone.
Last time I did anything with Prolog was over 20 years ago, so I don't remember anything Prolog specific.
However, if I were to do this in any functional-friendly language, I would sort all the sublists in the big list, and then remove all the duplicates.
I'm doing an exercise in Prolog using SWI-Prolog, to take a nested list and convert it into a list of elements and then make the sum of the elements, I made two separate predicates which make me both functionalities:
my_flatten(X,[X])->Transform a list, possibly holding lists as elements into a 'flat' list by replacing each list with its elements (recursively).
my_flatten(X,[X]) :- \+ is_list(X).
my_flatten([],[]).
my_flatten([X|Xs],Zs) :-
my_flatten(X,Y), my_flatten(Xs,Ys), append(Y,Ys,Zs).
addList-> Adds all the elements of a list and returns their sum
addList([],0).
addList([X|Xs],N):-
addList(Xs,N0),N is X+N0.
The problem itself is that I'm new programming in Prolog and I don't know how join both predicates in the same predicate, so that the list returned by my_flattern be used by the predicate addList. Maybe it's a bit foolish question, but I've been stuck on it for days. I need some help to clarify this question for other problems
To construct a new predicate which does both, simply call both my_flatten and addList, with a shared variable:
addListFlattened(L, Sum) :-
my_flatten(L, L2),
addList(L2, Sum).
addListFlattened is a new predicate (you can change the name), which reuses addList.