Reputation: 2335
sort([ [30,100], [10,11] ], X).
gets
X = [[10,11],[30,100]]
How can I sort only by the first index of each sublist?
i.e
X = [[10,100], [30, 11]]
Thanks
Upvotes: 1
Views: 3975
Reputation: 499
The below is my untested code.. there may be one/two cosmetic errors... The input list is split into two based on the head value on the list and the resulted two lists are recursively processed to finally result the sorted output.
sort(Input,Output):-sort(Input,[],Output).
sort([],SortedOut,SortedOut).
sort([[Index1,Index2]|Tail],SortedBig,Out):-
split(Tail,[Index1,Index2],LessList,BigList),
!,sort(BigList,SortedBig,NewSort),
sort(LessList,[[Index1,Index2]|NewSort],Out).
split([],[_D],[],[]).
split([[Index1,Index2]|Rem],[Index21,Index22],[[Index1,Index1]|L1],L2):-
Index1<Index21,
!,split(Rem,[Index21,Index22],L1,L2).
split([[Index1,Index2]|Rem],[Index21,Index22],L1,[[Index1,Index1]|L2]):-
!,split(Rem,[Index21,Index22],L1,L2).
Try this and let me know...
Upvotes: 0
Reputation: 7493
@chac(+1 btw): there's no need of lambda to one-line this (in swi at least!):
sortfirst(L, Res) :-
maplist(selectchk, X, L, R),
msort(X, XS),
maplist(selectchk, XS, Res, R).
but lambda versions or your first version are less tricky and more readable I think!
Upvotes: 2
Reputation: 60004
The simpler way should be perusing the available builtins. Then take the first element from each sublist, sort them, and replace in the original:
sortfirst(L, S) :-
maplist(get_first, L, A),
msort(A, B),
maplist(set_first, L, B, S).
get_first([E|_], E).
set_first([_|R], E, [E|R]).
edit: note that msort is required, to avoid losing duplicates.
test:
?- sortfirst([ [30,100], [10,11] ], X).
X = [[10, 100], [30, 11]].
get/set first are just needed to adjust the arguments from maplist: if we use lambda, we can write a true 'one liner' procedure:
:- [lambda].
sortfirst_lambda(L, S) :-
maplist(\X^Y^(X = [E|_], Y = E), L, A),
msort(A, B),
maplist(\X^Y^Z^(X = [_|R], Y = E, Z = [E|R]), L, B, S).
Simple identities can simplify a little that expressions:
sortfirst_lambda(L, S) :-
maplist(\X^Y^(X = [Y|_]), L, A),
msort(A, B),
maplist(\X^Y^Z^(X = [_|R], Z = [Y|R]), L, B, S).
edit: or still more simplified:
sortfirst_lambda(L, S) :-
maplist(\[Y|_]^Y^true, L, A),
msort(A, B),
maplist(\[_|R]^Y^[Y|R]^true, L, B, S).
Here you can see that, as in the original get/set first, just the unification of arguments is needed.
Thus lambda it's syntactically convenient, but has its cost:
?- randomlists(100000, 3, -30,+30, L),
time(sortfirst(L,A)),
time(sortfirst_lambda(L,B)),
assertion(A=B).
% 400,012 inferences, 0,482 CPU in 0,483 seconds (100% CPU, 830072 Lips)
% 1,700,012 inferences, 1,717 CPU in 1,721 seconds (100% CPU, 990302 Lips)
L = [[-8, -13, 11], [-13, -27, -29], [5, 10, -24], [-8, -7, -6], [3, -24, -9], [-13, -20, -24], [7, 27|...], [-5|...], [...|...]|...],
A = B, B = [[-30, -13, 11], [-30, -27, -29], [-30, 10, -24], [-30, -7, -6], [-30, -24, -9], [-30, -20, -24], [-30, 27|...], [-30|...], [...|...]|...].
here are the service predicates to build sized test data:
randomlist(Length, Low, High, List) :-
findall(E, (between(1, Length, _),
random(Low, High, E)), List).
randomlists(Length1, Length2, Low, High, ListOfLists) :-
findall(E, (between(1, Length1, _),
randomlist(Length2, Low, High, E)), ListOfLists).
Upvotes: 2