PROLOG. Get all objects from list matching a pattern - prolog

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

Related

How to predicate elements in a list based on element type

Given a list of the format,
[item(a), item(b), item(c), other(d), other(e) ...]
where the number of items isn't fixed, nor is the number of others, but items always precede other, how do I split the list so that I can pass the items and the others into different predicates.
I've been trying to find ways to split the list based on the elements but can't figure out a way to do so.
I need to write a predicate that will take this list, and then pass the items to an itemPredicate, and the others to an otherPredicate.
If there's any other info I can provide please let me know.
Let's start with a predicate to classify elements. What about
item_t(item(_), true).
item_t(other(_), false).
Note that this predicate has an extra argument for its truth value. It only accepts item(_) or other(_) elements. It fails entirely if something like unfit(x) is presented. Now imagine, we have a predicate takeWhilet/3 we could now write
?- takeWhilet(item_t, [item(a), item(b), item(c), other(d), other(e)], Xs).
takeWhilet(_P_1, [], []).
takeWhilet(P_1, [E|_], []) :-
call(P_1, E, false).
takeWhilet(P_1, [E|Es], [E|Fs]) :-
call(P_1, E, true),
takeWhilet(P_1, Es, Fs).
More beautifully using library(reif)s if_/3:
takeWhilet(_P_1, [], []).
takeWhilet(P_1, [E|Es], Fs0) :-
if_( call(P_1, E)
, ( Fs0 = [E|Fs], takeWhilet(P_1, Es, Fs) )
, Fs0 = [] ).
Now, we might define other_t/2 similarly...
A simple split predicate that separates item(_) from other(_) could work as follows:
split([], [], []).
split([item(A) | T], [item(A) | IT], OL) :- split(T, IT, OL).
split([other(A) | T], IL, [other(A) | OT]) :- split(T, IL, OT).
And use it like this:
?- split([item(1), other(2), other(3), item(4), other(5)], X, Y).
X = [item(1), item(4)],
Y = [other(2), other(3), other(5)].
It doesn't even require that items always precede others.
You can generalize to any type of items
work(item,L) :-
format('args of \'item\' ~w~n', L).
work(other,L) :-
format('args of \'other\' ~w~n', L).
work(anything, L) :-
format('args of \'anything\' ~w~n', L).
work_element(Item) :-
Item =.. [A|L],
work(A, L).
my_split(In) :-
maplist(work_element, In).
For example :
?- my_split([item(a), item(b), item(c), other(d), other(e) , item(f), other(g)]).
args of 'item' a
args of 'item' b
args of 'item' c
args of 'other' d
args of 'other' e
args of 'item' f
args of 'other' g
true

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

Write a Prolog predicate next(X,List,List1)

Prolog predicate next(X, List,List1), that returns in List1 the next element(s) from List that follows X, e.g., next(a,[a,b,c,a,d],List1), will return List1=[b,d].
I have tried following:
next(X, [X,Y|List], [Y|List1]) :- % X is the head of the list
next(X, [Y|List], List1).
next(X, [Y|List], List1) :- % X is not the head of the list
X \== Y,
next(X, List, List1).
next(_,[], []).
First, whenever possible, use prolog-dif for expressing term inequality!
Second, the question you asked is vague about corner cases: In particular, it is not clear how next(E,Xs,Ys) should behave if there are multiple neighboring Es in Xs or if Xs ends with E.
That being said, here's my shot at your problem:
next(E,Xs,Ys) :-
list_item_nexts(Xs,E,Ys).
list_item_nexts([],_,[]).
list_item_nexts([E],E,[]).
list_item_nexts([I|Xs],E,Ys) :-
dif(E,I),
list_item_nexts(Xs,E,Ys).
list_item_nexts([E,X|Xs],E,[X|Ys]) :-
list_item_nexts(Xs,E,Ys).
Let's see some queries!
?- next(a,[a,b,c,a,d],List1).
List1 = [b,d] ;
false.
?- next(a,[a,a,b,c,a,d],List1).
List1 = [a,d] ;
false.
?- next(a,[a,a,b,c,a,d,a],List1).
List1 = [a,d] ;
false.
Note that above queries succeed, but leave behind useless choicepoints.
This inefficiency can be dealt with, but I suggest figuring out more complete specs first:)
This version is deterministic for the cases given by #repeat using if_/3 and (=)/3. It shows how purity and efficiency can coexist in one and the same Prolog program.
next(E, Xs, Ys) :-
xs_e_(Xs, E, Ys).
xs_e_([], _E, []).
xs_e_([X|Xs], E, Ys) :-
if_(X = E, xs_e_xys(Xs, E, Ys), xs_e_(Xs, E, Ys)).
xs_e_xys([], _E, []).
xs_e_xys([X|Xs], E, [X|Ys]) :-
xs_e_(Xs, E, Ys).
%xs_e_xys([X|Xs], E, [X|Ys]) :- % alternate interpretation
% xs_e_([X|Xs], E, Ys).

How to check if a list is a non-empty sublist of another list in Prolog

I am trying to create an included_list(X,Y) term that checks if X is a non-empty sublist of Y.
I already use this for checking if the elements exist on the Y list
check_x(X,[X|Tail]).
check_x(X,[Head|Tail]):- check_x(X,Tail).
And the append term
append([], L, L).
append([X | L1], L2, [X | L3]) :- append(L1, L2, L3).
to create a list, in order for the program to finish on
included_list([HeadX|TailX],[HeadX|TailX]).
but I am having problems handling the new empty list that I am trying to create through "append" (I want to create an empty list to add elements that are confirmed to exist on both lists.)
I have found this
sublist1( [], _ ).
sublist1( [X|XS], [X|XSS] ) :- sublist1( XS, XSS ).
sublist1( [X|XS], [_|XSS] ) :- sublist1( [X|XS], XSS ).
but it turns true on sublist([],[1,2,3,4)
Since you're looking for a non-contiguous sublist or ordered subset, and not wanting to include the empty list, then:
sub_list([X], [X|_]).
sub_list([X], [Y|T]) :-
X \== Y,
sub_list([X], T).
sub_list([X,Y|T1], [X|T2]) :-
sub_list([Y|T1], T2).
sub_list([X,Y|T1], [Z|T2]) :-
X \== Z,
sub_list([X,Y|T1], T2).
Some results:
| ?- sub_list([1,4], [1,2,3,4]).
true ? a
no
| ?- sub_list(X, [1,2,3]).
X = [1] ? a
X = [2]
X = [3]
X = [1,2]
X = [1,3]
X = [1,2,3]
X = [2,3]
(2 ms) no
| ?- sub_list([1,X], [1,2,3,4]).
X = 2 ? a
X = 3
X = 4
(2 ms) no
Note that it doesn't just tell you if one list is a sublist of another, but it answers more general questions of, for example, What are the sublists of L? When cuts are used in predicates, it can remove possible valid solutions in that case. So this solution avoids the use of cut for this reason.
Explanation:
The idea is to generate a set of rules which define what a sublist is and try to do so without being procedural or imperative. The above clauses can be interpreted as:
[X] is a sublist of the list [X|_]
[X] is a sublist of the list [Y|T] if X and Y are different and [X] is a sublist of the list T. The condition of X and Y different prevents this rule from overlapping with rule #1 and greatly reduces the number of inferences required to execute the query by avoiding unnecessary recursions.
[X,Y|T1] is a sublist of [X|T2] if [Y|T1] is a sublist of T2. The form [X,Y|T1] ensures that the list has at least two elements so as not to overlap with rule #1 (which can result in any single solution being repeated more than once).
[X,Y|T1] is a sublist of [Z|T2] if X and Z are different and [X,Y|T1] is a sublist of T2. The form [X,Y|T1] ensures that the list has at least two elements so as not to overlap with rule #2, and the condition of X and Z different prevents this rule from overlapping with rule #3 (which can result in any single solution being repeated more than once) and greatly reduces the number of inferences required to execute the query by avoiding unnecessary recursions.
Here is what you an do:
mysublist(L,L1):- sublist(L,L1), notnull(L).
notnull(X):-X\=[].
sublist( [], _ ).
sublist( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
sublist( [X|XS], [_|XSS] ) :- sublist( [X|XS], XSS ).
Taking a reference from this:
Prolog - first list is sublist of second list?
I just added the condition to check if it was empty beforehand.
Hope this helps.
If order matters. Example [1,2,3] is sublist of [1,2,3,4] but [1,3,2] not.
You can do something like this.
sublist([],L).
sublist([X|L1],[X|L2]):- sublist(L1,L2)
I would use append :
sublist(X, []) :-
is_list(X).
sublist(L, [X | Rest]) :-
append(_, [X|T], L),
sublist(T, Rest).
Basically we can check if M is a sublist of L if M exists in L by appending something on its back and/or its front.
append([], Y, Y).
append([X|XS],YS,[X|Res]) :- append(XS, YS, Res).
sublist(_, []).
sublist(L, M) :- append(R, _, L), append(_, M, 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