Reputation: 59
I have a list of predicates:
[patient(204,4,2),patient(203,3,2),patient(303,7,3),patient(302,6,3),patient(404,12,4),patient(403,11,4),patient(504,16,5),patient(503,15,5)]
I want to have a list of lists depending on the 3rd argument of each predicate:
[ [patient(204,4,2),patient(203,3,2)] , [patient(303,7,3),patient(302,6,3)] , [patient(404,12,4),patient(403,11,4)] , [patient(504,16,5),patient(503,15,5)] ]
Upvotes: 1
Views: 118
Reputation: 4438
Copying my answer from https://swi-prolog.discourse.group/t/sorting-predicates-in-prolog/5618/19 here:
test_sort_group(Group) :-
Patients = [patient(204,4,2),patient(203,3,2),patient(304,8,3),patient(303,7,3),patient(404,12,4),patient(403,11,4),patient(504,16,5),patient(503,15,5),patient(1,1,1),patient(506,6,5)],
group_list_by_arg(3, Patients, Group).
group_list_by_arg(Arg, Lst, Group) :-
% Keeping duplicates
sort(Arg, @=<, Lst, [H|T]),
group_list_by_arg_main_(T, Arg, H, Group).
group_list_by_arg_main_(T, Arg, H, Group) :-
arg(Arg, H, ArgVal),
Upto = [H|Upto0],
group_list_by_arg_loop_(T, Arg, ArgVal, Upto0, Rem),
group_list_by_arg_rem_(Rem, Arg, Upto, Group).
% Reached end of groups
group_list_by_arg_rem_([], _, G, G).
group_list_by_arg_rem_([_|_], _, G, G).
group_list_by_arg_rem_([H|T], Arg, _Upto, Group) :-
% Assemble next group
group_list_by_arg_main_(T, Arg, H, Group).
% Reached end of sorted list
group_list_by_arg_loop_([], _, _, [], []).
group_list_by_arg_loop_([H|T], Arg, ArgVal, Group, Rem) :-
arg(Arg, H, ArgVal), !,
% Add element to current group
Group = [H|Group0],
group_list_by_arg_loop_(T, Arg, ArgVal, Group0, Rem).
% Finished this group, but there may be other elements
group_list_by_arg_loop_([H|T], _Arg, _ArgVal, [], [H|T]).
Result in swi-prolog:
?- time(bagof(G, test_sort_group(G), Gs)).
% 49 inferences, 0.000 CPU in 0.000 seconds (94% CPU, 599807 Lips)
Gs = [[patient(1,1,1)],[patient(204,4,2),patient(203,3,2)],[patient(304,8,3),patient(303,7,3)],[patient(404,12,4),patient(403,11,4)],[patient(504,16,5),patient(503,15,5),patient(506,6,5)]].
Upvotes: 0
Reputation: 28993
You can group_by/4
which works in mysterious ways and will get you a single sublist at a time. E.g. assuming your list is Patients
:
group_by(C, patient(A,B), member(patient(A,B,C), Patients), Group)
Then
Group = [patient(204,4), patient(203,3)]
I can't get "C" into the group, which is annoying because we'll have to put it back in later. Use findall/3
to get all the groups:
findall(C-Group,
group_by(C, patient(A,B), member(patient(A,B,C), _Patients), Group),
AllGroups)
That makes something of this shape, with the grouping value on the front of the sublists:
AllGroups = [
2-[patient(204,4), patient(203,3)],
3-[patient(303,7), patient(302,6)],
4-[patient(404,12), patient(403,11)],
5-[patient(504,16), patient(503,15)]
]
And then some post-processing to put C back in:
restoreC([], []).
restoreC([C-Group|CBs], [L|Ls]) :-
maplist({C}/[P1,P2]>>(P1=patient(A,B),P2=patient(A,B,C)), Group, L),
restoreC(CBs, Ls).
Tag ,restoreC(AllGroups, Result)
onto the end for:
Result = [
[patient(204,4,2), patient(203,3,2)],
[patient(303,7,3), patient(302,6,3)],
[patient(404,12,4), patient(403,11,4)],
[patient(504,16,5), patient(503,15,5)]
]
Upvotes: 1