Remove duplicates in backtracking - prolog

I have a predicate next which essentially removes numbers from a list, in the attempt to decrease the overall size of the list.
So for example I have a list:
[3,2,1]
next will remove certain values from the list, so it'll return something like this
[3,2] or [3,1] or [3] or [2,1] etc
I'm running a script to find all possible moves:
findall(T, next([2,3], T), U).
The problem is for a list which repeats values such as:
L = [1,1,1,1].
the call
findall(T, next([1,1,1,1], T), U).
will unify U with [[1,1,1], [1,1,1], [1,1,1], [1,1,1]]
Is there a way to make the Prolog understand that it is returning the same output multiple times?
next([_ | T], T).
next([H | Tin], [H | Tout]) :-
next(Tin, Tout).

It seems a works for setof/3
setof(T, next([1,1,1,1], T), U)
--- EDIT ---
The OP say
This does do it but it's a bit of a hacky fix, I'm looking for an alteration in the next predicate
I don't think the following it's a great solution and I suspect that it's better use setof/3 with the original next/2 but...
next(Lin, LLout) :-
nextH(Lin, [], LLout).
nextH([], _, []).
nextH([H | Tin], Pre, LLout1) :-
append(Pre, Tin, L),
append(Pre, [H], Pre0),
nextH(Tin, Pre0, LLout0),
( member(L, LLout0)
-> LLout1 = LLout0
; LLout1 = [L | LLout0] ).
--- EDIT 2 ---
The OP ask
how could you use setof in the next predicate?
If you use your original next/2 predicate that generate singles lists (but I recall it nextH, "next helper")
nextH([_ | T], T).
nextH([H | Tin], [H | Tout]) :-
nextH(Tin, Tout).
the next/2 predicate that return a list of unique list become simply (using setof/3)
next(Lin, LLout) :-
setof(Lout, nextH(Lin, Lout), LLout).

Related

Eliminate consecutive duplicates of list elements with prolog

The original problem was to come up with a way to get the following
remove([a,a,a,b,q,q,q,q,e,e,e]),X)
X = [a,b,q,e]
We can solve this problem by one iteration along the list. At any point in the list we check the current element and the next element, if they are the same then we ignore the current element, else if they are different we take the current element.
rm_dup([], []).
rm_dup([X], [X]).
rm_dup([X1, X2 | Xs], [X1 | Ys]) :-
dif(X1, X2), rm_dup([X2|Xs], Ys).
rm_dup([X, X | Xs], Ys) :-
rm_dup([X | Xs], Ys).
The first and second clauses are base clauses in which there are no duplicate elements. The third and fourth clauses are recursive rules.
In third clause we state that if the input list has two values X1 and X2 and they are different dif(X1, X2), then keep the current value.
In fourth clause if we have same consecutive values then we ignore the current value.
The third and fourth clauses are mutually exclusive and hence to make the predicate deterministic it is better to combine them as follows
rm_dup([X], [X]) :- !.
rm_dup([X1, X2 | Xs], Ys) :-
dif(X1, X2) -> (rm_dup([X2 | Xs], Ys1), Ys = [X1 | Ys1]);
rm_dup([X2 | Xs], Ys).
Even better is to just use equality as a condition and flip the then and else clauses.
rm_dup([X], [X]) :- !.
rm_dup([X1, X2 | Xs], Ys) :-
X1 = X2 -> rm_dup([X2 | Xs], Ys);
rm_dup([X2 | Xs], Ys1), Ys = [X1 | Ys1].
Kinda long, but this solved it.
delete(_,[],[]).
delete(X,[X|T],R):- delete(X,T,R).
delete(X,[H|T],[H|R]) :- delete(X,T,R).
remove([],[]).
remove([H|T], [H|R]) :-
member(H,T),!,
delete(H,T,R1),
remove(R1,R).
remove([H|T],[H|R]):-
remove(T,R).
You can simply use sort.
First: In the base class if the List are empty stop.
Second: In the second predicate, take the list and sort it. Display the answer in K.
remove_extras([],[]).
remove_extras([H|T],K):-
sort([H|T], K).
?-remove_extras([a,a,a,b,q,q,q,q,e,e,e],X).
X = [a, b, e, q]
?-remove_extras([1,3,2,5,1,6,1,2,7],X).
X = [1, 2, 3, 5, 6, 7]
If you don't want to use sort, you can use the following predicates:
Remove duplicates checks for elements existing more than once and keeps them in the list.
First: The base case says that if the lists are empty stop.
Second: The second predicate checks if an element exist in the remaining list (member) then don't add it to the final list.
Third: The third predicate says, if the element is in not the remaining list then add it to the final list.
remove_duplicates([],[]).
remove_duplicates([H | T], List) :-
member(H, T),
remove_duplicates( T, List).
remove_duplicates([H | T], [H|T1]) :-
\+ member(H, T),
remove_duplicates( T, T1).

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?
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...

Prolog checking if two lists are identical

So I need to write a prolog term called doubleAll7/2 that takes two lists. The program returns true if for every 7 that occurs in the first list, the second list has two sevens in a row.
Example doubleAll7([1,7,1],[1,7,7,1]) is true but doubleAll7([1,2,7],[1,2,7]) is false.
double7/2 doubles all occurrences of 7 in a list and works perfectly.
For some reason this program always returns false.
doubleAll7([H1|T1],[H2,T2]) :-
double7([H1|T1], L),
L == [H2|T2].
double7([],[]).
double7([H|T], [H,H|Z]) :-
H is 7,
!,
double7(T,Z).
double7([H|T], [H|Z]) :-
double7(T,Z).
There is an obvious error: the first row
doubleAll7([H1|T1],[H2,T2])
should be
doubleAll7([H1|T1],[H2|T2])
I mean: a | instead a , as separator between H2 and T2
But, sorry: I find you solution overcomplicated.
You can avoid double7/2 (your call to double7 is unusefull because, if I'm not wrong, it's equivalent to
doubleAll7(L1, L2) :-
double7(L1, L2).
), you can avoid is/2, you can avoi the ! and you can semplify all as
doubleAll7([], []).
doubleAll7([7 | T1], [7, 7 | T2]) :-
doubleAll7(T1, T2).
doubleAll7([H | T1], [H | T2]) :-
H \== 7,
doubleAll7(T1, T2).

How do I get every consecutive elements from a list in Prolog

Let say I have a list
[[a,b,c],[d,e,f],[g,h,i]]
I want to get every consecutive element and put it in another predicate.
func(a,b).
func(b,c).
func(d,e).
func(e,f).
func(g,h).
func(h,i).
I already wrote the predicate I want to put in, but I'm having a hard time getting two elements from the list of lists.
You can try :
consecutive(L, R) :-
maplist(create_func, L, RT),
flatten(RT, R).
create_func([A,B], [func(A,B)]) :- !.
create_func([A,B | T], [func(A,B) | R]) :-
create_func([B | T], R).

Prolog: Matching One or More Anonymous Variables

[_, [ X , _ ],_] will match a list like [d, [X,a], s]. Is there a way to match it to any pattern where there is one or more anonymous variables? ie. [[X,a],s] and [[d,a],[p,z], [X,b]] would match?
I am trying to write a program to count the elements in a list ie. [a,a,a,b,a,b] => [[a,4],[b,2]] but I am stuck:
listcount(L, N) :- listcountA(LS, [], N).
listcountA([X|Tail], [? [X, B], ?], N) :- B is B+1, listcountA(Tail, [? [X,B] ?], N).
listcountA([X|Tail], AL, N) :- listcountA(Tail, [[X,0]|AL], N).
Thanks.
A variable match a term, and the anonimus variable is not exception. A list is just syntax sugar for a binary relation, between head and tail. So a variable can match the list, the head, or the tail, but not an unspecified sequence.
Some note I hope will help you:
listcount(L, N) :- listcountA(LS, [], N).
In Prolog, predicates are identified by name and num.of.arguments, so called functor and arity. So usually 'service' predicates with added arguments keep the same name.
listcountA([X|Tail], [? [X, B], ?], N) :- B is B+1, listcountA(Tail, [? [X,B] ?], N).
B is B+1 will never succeed, you must use a new variable. And there is no way to match inside a list, using a 'wildcard', as you seem to do. Instead write a predicate to find and update the counter.
A final note: usually pairs of elements are denoted using a binary relation, conveniently some (arbitrary) operator. For instance, most used is the dash.
So I would write
listcount(L, Counters) :-
listcount(L, [], Counters).
listcount([X | Tail], Counted, Counters) :-
update(X, Counted, Updated),
!, listcount(Tail, Updated, Counters).
listcount([], Counters, Counters).
update(X, [X - C | R], [X - S | R]) :-
S is C + 1.
update(X, [H | T], [H | R]) :-
update(X, T, R).
update(X, [], [X - 1]). % X just inserted
update/3 can be simplified using some library predicate, 'moving inside' the recursion. For instance, using select/3:
listcount([X | Tail], Counted, Counters) :-
( select(X - C, Counted, Without)
-> S is C + 1
; S = 1, Without = Counted
),
listcount(Tail, [X - S | Without], Counters).
listcount([], Counters, Counters).
I'll preface this post by saying that if you like this answer, consider awarding the correct answer to #chac as this answer is based on theirs.
Here is a version which also uses an accumulator and handles variables in the input list, giving you the output term structure you asked for directly:
listcount(L, C) :-
listcount(L, [], C).
listcount([], PL, PL).
listcount([X|Xs], Acc, L) :-
select([X0,C], Acc, RAcc),
X == X0, !,
NewC is C + 1,
listcount(Xs, [[X0, NewC]|RAcc], L).
listcount([X|Xs], Acc, L) :-
listcount(Xs, [[X, 1]|Acc], L).
Note that listcount/2 defers to the accumulator-based version, listcount/3 which maintains the counts in the accumulator, and does not assume an input ordering or ground input list (named/labelled variables will work fine).
[_, [X, _], _] will match only lists which have 3 elements, 1st and 3rd can be atoms or lists, second element must be list of length 2, but i suppore you know that. It won't match to 2 element list, its better to use head to tail recursion in order to find element and insert it into result list.
Heres a predicate sketch, wich i bet wont work if copy paste ;)
% find_and_inc(+element_to_search, +list_to_search, ?result_list)
find_and_inc(E, [], [[E, 1]]);
find_and_inc(E, [[E,C]|T1], [[E,C1]|T2]) :- C1 is C+1;
find_and_inc(E, [[K,C]|T1], [[K,C]|T2]) :- find_and_inc(E, T1, T2).

Resources