Removing list within lists based on nth element - prolog

I've got a list of lists, for example:
[[1, 2, 3, 2], [1, 3, 4, 3], [1, 4, 5, 4], [2, 3, 5, 6], [1, 5, 6, 5],
[2, 4, 6, 8], [1, 6, 7, 6], [2, 5, 7, 10], [3, 4, 7, 12], [2, 6, 8, 12]]
I'd like to get the last element of and check to see if is the same as the 4th element of any of the other lists. If it is the same then leave the list alone, but if it is unique then remove the list. So in the example above I would be left with:
[[3, 4, 7, 12], [2, 6, 8, 12]]
Essentially I want to be remove all the lists where the last element is unique.
I've written a predicate to get the nth element:
my_membership(X, [X|_]).
my_membership(X, [_|Tail]) :-
my_membership(X, Tail).
where:
my_membership([_,_,_,Fourth],[[3, 4, 7, 12], [2, 6, 8, 12]]).
gives:
Fourth = 12
Fourth = 12

Start by building two basic predicates:
last([X], X).
last([_|T], X) :- last(T, X).
forth([_,_,_,F|_], F).
The first predicate extracts the last element of a list; the second predicate extracts the forth element of a list.
Now you can make a predicate that counts how many tomes an element X appears in forth place in any of the lists of a list of lists. Below, H in [H|T] is a list:
matching_forth([], _, 0).
matching_forth([H|T], X, R) :- forth(H, X), matching_forth(T, X, RR), R is RR + 1.
matching_forth([_|T], X, R) :- matching_forth(T, X, R).
With these predicates in place you can build a predicate for checking your condition. It will have three clauses - for a situation when the list is empty, for when the head list has a matching forth element in another list, and for situations when it doesn't:
my_membership([], [], _).
my_membership([H|T], [H|R], A) :-
last(H, X), matching_forth(A, X, C), C > 1, my_membership(T, R, A).
my_membership([_|T], R, A) :- my_membership(T, R, A).
The first and last clauses are self-explanatory. The middle clause extracts the last element from the head list, counts how many times it matches the forth element in the original list of lists (A stands for "all"), and adds H to the result when there is a match. Adding happens through unification with the head of the result list.
Finally, you need a my_membership/2 predicate to start off the recursive chain that passes along the original list of lists:
my_membership(L, R) :- my_membership(L, R, L).
Demo.

Here's a different twist on a potential solution. It uses an accumulator to collect members that we've seen already and checks along the way. The result saves those that have either been seen or are currently in the tail. It requires the use of the built-in, memberchk/2.
my_membership(L, R) :-
my_membership(L, [], R).
my_membership([], _, []).
my_membership([X|T], Acc, R) :-
X = [_,_,_,D],
( memberchk([_,_,_,D], Acc)
-> R = [X|T1],
Acc1 = Acc
; memberchk([_,_,_,D], T)
-> R = [X|T1],
Acc1 = [X|Acc]
; R = T1,
Acc1 = Acc
),
my_membership(T, Acc1, T1).
| ?- my_membership([[1, 2, 3, 2], [1, 3, 4, 3], [1, 4, 5, 4], [2, 3, 5, 6], [1, 5, 6, 5],
[2, 4, 6, 8], [1, 6, 7, 6], [2, 5, 7, 10], [3, 4, 7, 12], [2, 6, 8, 12]], L).
L = [[2,3,5,6],[1,6,7,6],[3,4,7,12],[2,6,8,12]]
yes

Related

How to create a list without using findall? Prolog

I am generating permutations:
takeout(X,[X|T],T).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S).
perm([],[]).
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
I want to know how to create a list of all the permutations without using findall.
Example:
?-perm([1,2,3],List).
List = [[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]
Group permutations by the element it starts with.
Take an element X and create permutations Ys1 without it in the original list.
Adding this element X as the first element of all these permutations we have the list XP of permutations starting with X.
Appending all the groups will give you all permutations.
cons(X, Xs, [X|Xs]).
perm([], [[]]).
perm(Xs, Ys) :-
dif(Xs, []),
maplist({Xs}/[X, XP]>>(select(X, Xs, Xs1),
perm(Xs1, Ys1),
maplist(cons(X), Ys1, XP)),
Xs, Yss),
append(Yss, Ys).
?- perm([1, 2, 3], X).
X = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] ;
false.
?- length(Y, 8), perm(Y, X), length(X, N). %8 factorial
N = 40320
The idea is to generate permutations and test if you already created this permutation. I'm using the inbuild predicate permutation/2.
perm(Ori,Out):-
perm(Ori,[],Out).
perm(Ori,Acc,Ret):-
permutation(Ori,Perm),
\+ member(Perm,Acc),
!,
perm(Ori,[Perm|Acc],Ret).
perm(_,L,L).
?- perm([1,2,3],E).
E = [[3, 2, 1], [3, 1, 2], [2, 3, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3]].
The code is not the fastest one since it checks multiple times for membership.

Prolog - sort sublists of a list

I want to sort the sublists of a list which contains integers eliminating the duplicates. Example:
[1, 2, [4, 1, 4], 3, 6, [7, 10, 1, 3, 9], 5, [1, 1, 1], 7]
=>>>
[1, 2, [1, 4], 3, 6, [1, 3, 7, 9, 10], 5, [1], 7].
I know that i have to work with functor s(but i didn't really get it).
Here is my code : (the (insert+sorting) function works in a simple list of integers,but don't work here. i'm getting red false everytime)
insert(E,[],[E]).
insert(E,[H|T],[H|L]):-
E>H,
insert(E,T,L).
insert(E,[H|T],[H|T]):-
E=H,
!.
insert(E,[H|T],[E|[H|T]]):-
E<H,
!.
sort([],[]).
sort([i(H)|T],L):-
sort(T,L1),
insert(i(H),L1,L).
You could try a solution like this, it uses the the sort/2 predicate to sort sublists:
sort_sublists([], []).
sort_sublists([X|Xs], List) :-
(integer(X) ->
List = [X | List1]
;
sort(X, Sorted),
List = [Sorted | List1]
),
sort_sublists(Xs, List1).
Example call:
?- set_prolog_flag(answer_write_options,[max_depth(0)]).
true.
?- sort_sublists([1, 2, [4, 1, 4], 3, 6, [7, 10, 1, 3, 9], 5, [1, 1, 1], 7], X).
X = [1,2,[1,4],3,6,[1,3,7,9,10],5,[1],7].
#Eduard Adrian it's hard to answer in comments, so first thing you need to do is to remove duplicates from the nested lists.
Here i tried that and you can see different cases that you need to handle. One example:(Head of your list can be a list but it's tail will be empty this happends if last element of your list is a list) in that case you need to define another predicate which will match your recursive call.
Once you have removed duplicates you can use simple sorting algorithm but you have to check if the head is a list then you sort innner list first and place it to the same place otherwise call sort predicate.
As you asked how check if an element is_list or integer, for that you can always use built-in because those predicates you can't write by yourself.

How to permute in Prolog the elements of a list composed of sub-list and atoms

Let us consider lists such as L=[[7,3,4],9,[4,5],[1,3,5],4] where components could be lists or atoms. How to produce this type of permutation results:
R = [[7, 3, 4], 9, [4, 5], [1, 3, 5], 4]
R = [[7, 4, 3], 9, [5, 4], [1, 3, 5], 4]
R = [[7, 4, 3], 9, [4, 5], [1, 5, 3], 4]
....
In fact, we would like to have all the possible permutations of the sub-lists of L while the atom should remain unchanged.
I wrote a classical predicate permutation(OriginalList,PermutedList), and an allperm predicate that apply the maplist function to this permutation predicate:
permutation([],[]).
permutation(L,[T|Q]) :-
select(T,L,L1),
permutation(L1,Q).
/*L is the list to permute, R the result*/
allperm(L,R) :-
maplist(permutation,L,R).
It functions only in the special case where L is composed of lists and doesn't function when L is heterogeneous (atoms and lists).
Could you provide hints or elements of solution to do the right permutation?
The following modification should do:
permutation(X,X).
permutation(L,[T|Q]) :-
dif(L,[T|Q]),
select(T,L,L1),
permutation(L1,Q).
allperm(L,R) :-
maplist(permutation,L,R).
We extend the “identity” permutation to any Prolog term (first clause of permutation) and disallow it in the second clause (which can only be applied to lists anyway).

Find cycle of permutation in Prolog

I'm new in Prolog world. I want to find out if a permutation is 'one-cycle'.
I'm trying to write a predicate to generate cycle from permutation. Here is my code (not working):
find_next([E|_], [N|_], E, N).
find_next([_|L1], [_|L2], E, N) :-
find_next(L1, L2, E, N).
find_cycle(L1, L2, E, C) :-
append(C, [E], C1),
find_next(L1, L2, E, N),
find_cycle(L1, L2, N, C1).
Permutations are represented by two lists (for example: [1, 2, 3, 4], [3, 4, 2, 1]).
find_next generates next cycle element (N) for element (E) (for example: E=1, N=3).
find_cycle looks for cycle (C) starting from element E.
Unfortunately I don't know how to stop my recurrence when find_next returns N same as first element of cycle C.
EDIT: some examples.
find_cycle([1, 2, 3, 4], [3, 4, 2, 1], 1, X).
should return:
X = [1, 3, 2, 4];
false.
and:
find_cycle([1, 2, 3, 4], [4, 2, 1, 3], 1, X).
should return:
X = [1, 4, 3];
false.
Why?
It is simple decomposition of permutation into disjointed cycles.
Let's analyze second permutation: [1, 2, 3, 4], [4, 2, 1, 3].
Take first element: 1.
1 goes into 4
4 goes into 3
3 goes into 1
end of cycle.
This permutation is not decomposable into one cycle (length of generated cycle is smaller than length of permutation).
To find all the cycles of the permutation:
perm_to_cycles(Perm, NPerm, Cycles):-
perm_struct(Perm, NPerm, PermS),
perm_to_cycles(PermS, [], [], Cycles),
!.
perm_to_cycles([], _, Cycles, Cycles).
%perm_to_cycles([p(Id, Id)|PermS], _, InCycles, Cycles):-
% perm_to_cycles(PermS, [], InCycles, Cycles). % This clause would remove fixed elements
perm_to_cycles([p(Id, Item)|PermS], Cycle, InCycles, Cycles):-
(select(p(Item, NId), PermS, NPermS) ->
perm_to_cycles([p(Item, NId)|NPermS], [Id|Cycle], InCycles, Cycles) ;
(
reverse([Id|Cycle], RCycle),
perm_to_cycles(PermS, [], [RCycle|InCycles], Cycles)
)
).
perm_struct([], [], []).
perm_struct([Item|Perm], [NItem|NPerm], [p(Item, NItem)|PermS]):-
perm_struct(Perm, NPerm, PermS).
The commented clause would remove fixed elements of list of cycles.
To get only one-cycle permutations you can constrain the third argument to be a one-element list. For example:
?- perm_to_cycles([1, 2, 3, 4], [3, 4, 2, 1], [X]).
X = [1, 3, 2, 4]
?- perm_to_cycles([1, 2, 3, 4], [4, 2, 1, 3], [X]).
false.
?- perm_to_cycles([1, 2, 3, 4], [4, 2, 1, 3], X).
X = X = [[2], [1, 4, 3]].
-Hi Dave, here is my solution to the problem. I followed your instructions like 1 goes to 4 , 4 goes to 3 etc and here is what I came up with. First I create arcs between the elements of the two lists(permutations) and then I simply move through the created graph using find_cycle (until our nodes start repeating ). I tried to use variable names that are self explanatory but if have hard time understanding the code let me know.
create_arcs([],[],[]).
create_arcs([H|T],[H1|T1],[arc(H,H1)|RezArc]) :- create_arcs(T,T1,RezArc).
find_cycle(Perm,Perm2,E,X) :- create_arcs(Perm,Perm2,Arcs),
find_cycle(E,Arcs,[],X).
find_cycle(StartNode,Arcs,LocRez,LocRez) :- member(arc(StartNode,NextNode),Arcs),
member(StartNode,LocRez).
find_cycle(StartNode,Arcs,LocRez,FinalRez) :- member(arc(StartNode,NextNode),Arcs),
not(member(StartNode,LocRez)),
append(LocRez,[StartNode],LocRezNew),
find_cycle(NextNode,Arcs,LocRezNew,FinalRez).

Prolog insertion sort

There is a simple Prolog insertion sort alghoritm:
sorting([A|B], Sorted) :- sorting(B, SortedTail), insert(A, SortedTail, Sorted).
sorting([], []).
insert(A, [B|C], [B|D]) :- A #> B, !, insert(A, C, D).
insert(A, C, [A|C]).
It does well on normal lists:
?- sorting([5, 4, 9, 1, 3, 8], X).
X = [1, 3, 4, 5, 8, 9].
But I also need to sort sublist of list contains any of them:
?- sorting([2, 5, [5, 4, 3], [6, 3], 4, 8], X).
X = [2, 4, 5, 8, [5, 4, 3], [6, 3]].
Is what return now. And
?- sorting([2, 5, [5, 4, 3], [6, 3], 4, 8], X).
X = [2, 4, 5, 8, [3, 4, 5], [3, 6]].
what I need to return. So how can I sort sublist too? Thanks in advance!
I offer this simple solution:
Insert element in the sorted list
insert(X, [], [X]):- !.
insert(X, [X1|L1], [X, X1|L1]):- X=<X1, !.
insert(X, [X1|L1], [X1|L]):- insert(X, L1, L).
Use principe of insertion sort algorithm
insertionSort([], []):- !.
insertionSort([X|L], S):- insertionSort(L, S1), insert(X, S1, S).

Resources