What does unique means in prolog - prolog

I am trying to understand the unique predicate:
for example:
unique([H|T],[H|TU]) :- delete(T,E,TN), unique(TN,TU).
delete(T,E,TN) is true if a list TN is identical to a list T
after removing all occurrences of an element E.
delete([H|T],H,TN) :- delete(T,H,TN).
delete([H|T],E,[H|TN]) :- \+ H=E, delete(T,E,TN).
delete([],_,[]).
unique(['a','b','a','c'],X).
X = [a, b, c] <- Enter was pressed
Yes
So it comparing two lists
T = ['a', 'b', 'd', 'f', 't']
TN =['a', 'b', 'd']
removes the occurrences of common elements
This is the entire code:
% delete(L,E,LN) is true if a list LN is identical to a list L
% after removing all occurences of an element E.
delete([H|T],H,TN) :- delete(T,H,TN).
delete([H|T],E,[H|TN]) :- \+ H=E, delete(T,E,TN).
delete([],_,[]).
% unique(L,LN) is true if a list LN is identical to a list L
% without all repeated elements.
unique([H|T],[H|TU]) :- delete(T,E,TN), unique(TN,TU).
unique([],[]).
please guide here
thanks a lot

First of all you need to change
unique([H|T],[H|TU]) :- delete(T,E,TN), unique(TN,TU).
to:
unique([H|T],[H|TU]) :- delete(T,H,TN), unique(TN,TU).
What unique predicate does is to delete element duplicates in the list and keep only the first occurrence. But your predicate fails because you don't handle the case of empty list so if you add the clause:
unique([],[]).
It works fine:
?- unique([a,b,a,c],X).
X = [a, b, c].
In this example it calls unique([a,b,a,c],X). which calls delete([b,a,c],a,TN) and TN becomes [b,c] , calls unique([b,c],TU) and finally returns [a|TU].
TU is found recursively with the same way by calling:unique([b,c],TU)
which returns TU=[b|TU2] where TU2 given recursively by calling: unique([c],TU2) which returns [c].

Related

Prolog - Using append to return a single list

Write a predicate triangle(Bs, Ds) where Bs a list of the positions of the foo and Ds is the (single) list of differences in position. Use the built-in predicate append and the your own distances predicate.
This is related to this questions :
Build a list with abs() in prolog
distances([], _, []).
distances([H|T], B, [D|Ds]) :- abs(H - B, D), distances(T, B, Ds).
triangle([],[]).
triangle([H|T], [D|Dt]) :- distances(T,H,D), triangle(T,Dt).
?- triangle([1,2,3],A).
A = [[1, 2], [1], []].
The Solution I require
?- triangle([1,2,3],A).
A = [1,2,1].
The answer is correct but it is a in list of lists.
I'm having trouble turning Ds into a single list. I have tried using append in various positions within the predicate but either get repetitions or the predicate evaluates to false. How can I turn Ds into a single list [1,2,3] with append?
You can append the list D with Dt, instead of using [D|Dt] where you thus prepend the list with one element D:
triangle([],[]).
triangle([H|T], Ds) :-
distances(T, H, D),
append(D, Dt, Ds),
triangle(T, Dt).

Prolog question, manipulate list of lists

I'm new to Prolog and have a question:
Suppose I have a list of lists; [list1, list2, list3,..., list_n]
If list_j contains list_i, i.e. every variable in list_i occurs in list_j, then remove list_j.
For example, if the input is
[[a,b,c], [a,c], [a,d], [a,d,e]]
the output should be
[[a,c], [a,d]]
because [a,b,c] contains [a,c] and [a,d,e] contains [a,d].
How to implement this in SWI-Prolog? Any help is appreciated.
First of all, SWI already has a predicate to check whether a list is "contained" (in terms of set-inclusion) in another list: subset/2 (http://www.swi-prolog.org/pldoc/man?predicate=subset/2).
Now you can use that to check whether or not to remove a specific list from all lists:
remove(Lists, List) :-
member(List, Lists),
member(Sublist, Lists),
Sublist \= List,
subset(Sublist, List).
Read as: remove List from Lists if it is a member of Lists and there is also another (confirmed by \=) member of Lists (call it Sublist), which is a subset of List.
?- remove([[a,b,c], [a,c], [a,d], [a,d,e]], L).
L = [a, b, c] ;
L = [a, d, e] ;
Now you can use that to answer your original question:
remaining(Lists, Remaining) :-
bagof(List,
(
member(List, Lists),
\+ remove(Lists, List)
),
Remaining).
Let these lists be Remaining from Lists which are members of the original list of Lists and are not (\+) to be removed.
?- remaining([[a,b,c], [a,c], [a,d], [a,d,e]], Remaining).
Remaining = [[a, c], [a, d]].

PROLOG defining 'delete' predicate

delete(X,[X|R],[_|R]).
delete(X,[F|R],[F|S]) :-
delete(X,R,S).
Above is my definition of delete predicate, for delete(X,L,R), intended to delete every occurrence of X in L with result R.
I had queried below, and get "G2397797". What does this string stand for?
?- delete(1,[1,2,3,4,5],X).
X = [_G2397797, 2, 3, 4, 5] .
If you simply correct your first clause and remove the unnecessary anonymous variable, you would get:
delete_each(X, [X|L], L).
delete_each(X, [Y|Ys], [Y|Zs]) :-
delete_each(X, Ys, Zs).
This will use unification, and delete each occurrence of X in the list upon backtracking:
?- delete_each(a, [a,b,a,c], R).
R = [b, a, c] ;
R = [a, b, c] ;
false.
Do you see how this is identical to select/3?
If you want to delete all occurrences of X in the list, you can see the answer by #coder.
In the answer you get X = [_G2397797, 2, 3, 4, 5] . , _G2397797 is not a string it is a variable that is not instantiated. This is due to the clause:
delete(X,[X|R],[_|R]).
which places in the output list an anonymous variable "_". You could write delete(X,[X|R],R).
But this has multiple problems. Firstly it only deletes the first occurrence of X not all because in the above clause when you find one you succeed. Also you haven't thought the case of empty list which is also the base case of the recursion. Finally in your second clause you haven't applied any rule that says F and X differ and this clause give wrong results when F equals to X.
So you could write:
delete(_,[],[]).
delete(X,[X|R],S):-delete(X,R,S).
delete(X,[F|R],[F|S]):-dif(X,F),delete(R,S).

Splitting a prolog list based on a delimiter from the list?

In Prolog, let's say I have a list such as
[fun, joke1, joke2, fun, joke3, fun, joke4, joke5, joke6]
I'm trying to build a list of lists that will result to
[ [joke1, joke2], [joke4, joke5, joke6] ]
using fun as the delimiter and ignoring a built list of length 1, hence
[joke3]
is not inside that list.
I tried using split_string but it doesn't work for what I need to obtain. I've also tried recursion using append but that didn't work out as well. Hoping I can be pointed to the right direction.
Thanks!
Here is a solution which uses two predicates:
split_on_delimiter(L, D, S) :-
split_on_delimiter_(L, D, R),
findall(X, (member(X, R), length(X,Length), Length > 1), S).
split_on_delimiter_([], _, [[]]).
split_on_delimiter_([D|T], D, [[]|T2]) :-
split_on_delimiter_(T, D, T2).
split_on_delimiter_([H|T], D, [[H|T2]|T3]) :-
dif(H, D),
split_on_delimiter_(T, D, [T2|T3]).
split_on_delimiter_/3 is the predicate which really splits the list based on the delimiter D. split_on_delimiter/3 is the predicate that you would call and which will in turn call split_on_delimiter_/3.
To keep only the sublists of length more than 1, we use findall to find all sublists of the result of the split which have Length > 1.
split_on_delimiter_/3 itself is fairly simple:
First rule: spliting an empty list results in only one sublist: the empty list.
Second rule: when the first element of the list is the delimiter, the result is the empty list followed by the result of the recursive call.
Third rule: when the first element of the list is not the delimiter (dif(H, D)), then we put that element at the beginning of the first sublist and recursive call.
An example:
?- split_on_delimiter([fun, joke1, joke2, fun, joke3, fun, joke4, joke5, joke6], fun, Z).
Z = [[joke1, joke2], [joke4, joke5, joke6]] ;
false.
Note
split_on_delimiter_/3 has extraneous choice points (which is why you can press ; after the first result, because it thinks there can be more answers but there are none). You could solve this using ! or once.
A better solution to remove those choice points is using if_/3 and (=)/3 of module(reif) (though I doubt this is useful to you):
split_on_delimiter_(L, D, [L2|T2]) :-
if_(L = [],
[L2|T2] = [[]],
( L = [H|T],
if_(H = D,
( L2 = [],
split_on_delimiter_(T, D, T2)
),
( L2 = [H|T3],
split_on_delimiter_(T, D, [T3|T2])
)
)
)
).

Pattern matching list of lists

I have a problem where I have a list like this:
[[el1, el2, el3],
[el4, el5, el6],
[[el7, el8, el9], [el10, el11, el12], ..... , [elxx, elyy, elzz]],
[el, el, el]...]]
I want to pattern match the inner list of lists, the
[el7, el8, el9], [el10, el11, el12], ..... , [elxx, elyy, elzz]
How can this be done?
As of now I patternmatch the other elements with
my_method([[El1, El2, El3] | Rest]).
UPDATE
I want to pattern match if the next item of the list is a list of lists - I will be iterating over this list, removing item after item. There can be any number of lists of lists, and they can contain any number of items. They can also contain lists of lists. In fact, I will recursively call the same processing method whenever I come upon a list of lists.
All bottom level lists will have three elements, however these elements might be different:
[1, p, neg(5,6)]
[5, neg(7,6), assumption]
You said "I will be iterating over this list, removing item after item", so here's code that does just that, assuming an "item" is a three-element list of non-lists.
nested_member(X,X) :-
X = [A,_,_],
\+ is_list(A).
nested_member(X,[L|_]) :-
nested_member(X,L).
nested_member(X,[_|L]) :-
nested_member(X,L).
This can be used to backtrack over the "items":
?- nested_member(X,[[el1, el2, el3], [el4, el5, el6],
[[el7, el8, el9], [el10, el11, el12],[elxx, elyy, elzz]]]).
X = [el1, el2, el3] ;
X = [el4, el5, el6] ;
X = [el7, el8, el9] ;
X = [el10, el11, el12] ;
X = [elxx, elyy, elzz] ;
false.
I you want, you can even find out how deep in the list the items were found:
nested_member(X,L,D) :-
nested_member(X,L,0,D).
nested_member(X,X,D,D) :-
X = [A,_,_],
\+ is_list(A).
nested_member(X,[L|_],D0,D) :-
D1 is D0+1,
nested_member(X,L,D1,D).
nested_member(X,[_|L],D0,D) :-
nested_member(X,L,D0,D).
You can use predicates similar to the following.
qualify([], []).
qualify([H|T], [HN|TN]) :- qualify_one(H, HN), qualify(T, TN).
qualify_one([H|_], N) :- qualify_one(H, N1), N is N1 + 1, !.
qualify_one(_, 0).
What qualify does is for each member of the list to find out on what level of the scale “not a list”, “simple list”, “list of lists”, … it is, based on the first item.
Example:
?- qualify([1,[2,3,3],[[4,5,6], [7,8,9]]], NS).
NS = [0, 1, 2].

Resources