Prolog checking if two lists are identical - prolog

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

Related

PROLOG. Get all objects from list matching a pattern

I am a beginner in prolog and i have a problem with getting objects from list matching a pattern.
If i have a list [1,2,3,4,5,1,1] . I want to use a predicate selectAll(Elem,List,X).
Where i use ?- selectAll(1,[1,2,3,4,5,1,1],X), I get X =[1,1,1], but i also want to use data structures inside the predicate, not only atoms.
I originally wrote this predicate for getting all matching elements, but it works only for simple cases, where only atoms are used:
selectAll(_, [], []).
selectAll(X, [X | LIST], [X | RES]):-
selectAll(X, LIST, RES),!.
selectAll(X, [H | LIST], RES):-
selectAll(X, LIST, RES).
When i use this test predicate, everything works fine. I get X=[1,1,1], the result i want.
test_select_all:-
selectAll(1, [1,2,3,4,5,1,1], X),
write(X),nl,
fail.
I have a data structure called kv_pairs(A,B) where A and B contain atoms of any type.
So when i use the selectAll predicate for this datatype, i get unwanted results. X = [kv_pair(1,a)]. It selects only 1 element at most.
test_select_all_dict:-
selectAll(kv_pair(1,_), [kv_pair(1, a), kv_pair(1, b),kv_pair(3, jkak), kv_pair(15, asdjk), kv_pair(1, c)], X),
write(X),nl,
fail.
I then created this predicate, specifically for finding list elements, where all types are kv_pairs
selectAll(_, [], []).
selectAll(kv_pair(Arg, _), [kv_pair(Arg,_) | LIST], [kv_pair(Arg,_) | RES]):-
selectAll(kv_pair(Arg, _), LIST, RES),!.
selectAll(kv_pair(Arg, X), [kv_pair(A, B) | LIST], RES):-
selectAll(kv_pair(Arg, X), LIST, RES).
But then i get also unwanted results.
X = [kv_pair(1,_8378),kv_pair(1,_8396),kv_pair(1,_8426)]
How can i get
X = [kv_pair(1,a),kv_pair(1,b),kv_pair(1,c)]?
Any help would be appreciated.
You can use the ISO predicate subsumes_term/2 to undo bindings after unification:
select_all(Pattern, List, Result) :-
select_all_loop(List, Pattern, Result).
select_all_loop([], _, []).
select_all_loop([X|Xs], P, R) :-
( subsumes_term(P, X)
-> R = [X|Ys]
; R = Ys ),
select_all_loop(Xs, P, Ys).
Examples:
?- select_all(kv_pair(1,_), [kv_pair(1,a), kv_pair(1,b), kv_pair(3,c), kv_pair(4,d), kv_pair(1,c)], R).
R = [kv_pair(1, a), kv_pair(1, b), kv_pair(1, c)].
?- select_all(p(1,Y), [p(1,a), p(1,b), p(2,b), p(1,c)], L).
L = [p(1, a), p(1, b), p(1, c)].
?- select_all(p(X,b), [p(1,a), p(1,b), p(2,b), p(1,c)], L).
L = [p(1, b), p(2, b)].

How to swap the result of a predicate to the opposite? True to False, and vise versa

I need to make a function that returns True, if a list has no duplicates, and False if it does.
Use of appendTo is mandatory. I've managed to make a function, but it is basically the opposite result:
True if it has duplicates,
False if it doesn't
Now I need to swap the results somehow...
I've tried using not(), but it didn't work as I expected.
Maybe someone can help me? :)
appendTo([ ], L, L).
appendTo([X|L1], L2, [X|L]):- appendTo(L1, L2, L).
no_duplicates(List) :-
appendTo(Start, [Current | End], List),
(appendTo(_, [Current | _], Start) ; appendTo(_, [Current | _], End)).
You forgot to tell the recursion when to stop (first line):
appendTo([],L2,L2).
appendTo([X|L1], L2, [X|L]):- appendTo(L1, L2, L).
duplicates(List) :-
appendTo(Start, [Current | End], List),
( appendTo(_, [Current | _], Start)
; appendTo(_, [Current | _], End)
).
no_duplicates(List) :-
\+ duplicates(List).
Test cases:
?- no_duplicates([a,b,c,b]).
false.
?- no_duplicates([a,b,c]).
true.
Tested with Swish. Also \+/1 is the predicate you should use instead of not/1; it means as much as "cannot be proven".
Using only one predicate is a bit more tricky because of the negation. This one should work:
no_duplicates(List) :-
appendTo(Start, [Current | End], List),
( appendTo(_, [Current | _], Start)
; appendTo(_, [Current | _], End)
),
!,
fail.
no_duplicates(_).
Test cases
?- no_duplicates([a,b,c,b]).
false.
?-no_duplicates([a,b,c]).
true.
The cut (!) prevents further backtracking.

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

Remove duplicates in backtracking

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

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