This is a followup to an answer to a question about sorting on a particular argument of a term, without creating a new list for a keysort
(if I understood the original question correctly).
Say we wanted predsort/3
to behave exactly as sort/2
: if I understand correctly, this would mean calling it as:
?- predsort(compare, List, Sorted).
Now say that we wanted to use predsort/3
to sort as implemented by msort/2
(see also this question). One way to do it would be to define a comparison predicate Pred(-Delta, +A, +B)
that does not unify Delta
with =
when the elements are actually equal:
mcompare(Delta, A, B) :-
compare(Delta0, A, B),
( Delta0 == (=)
-> Delta = (<)
; Delta = Delta0
).
?- predsort(mcompare, List, Sorted).
Question: does that really simply sort without removing duplicates, as msort/2
does? It seems like it should.
Moving on: say we wanted to sort terms with arity > n, on the standard order of the nth argument in the term. The clean way to do it would be:
sort_argn(N, List, Sorted) :-
map_list_to_pairs(arg(N), List, Pairs),
keysort(Pairs, Sorted_pairs),
pairs_values(Sorted_pairs, Sorted).
If we wanted to use predsort/3
to achieve the same effect, we could try using a comparison predicate as follows:
compare_argn(N, Delta, A, B) :-
arg(N, A, AN),
arg(N, B, BN),
compare(Delta, AN-A, BN-B).
And to sort on the second argument:
?- predsort(compare_argn(2), List, Sorted).
However, this is not the same as sort_argn/3
above that uses keysort/2
. It will remove duplicates, and it will order compound terms according to the standard order of the original full term if the second arguments of two terms happen to be equal:
?- predsort(compare_argn(2), [f(b,2), f(a,1), f(a,1), f(a,2)], Sorted).
Sorted = [f(a, 1), f(a, 2), f(b, 2)].
?- sort_argn(2, [f(b,2), f(a,1), f(a,1), f(a,2)], Sorted).
Sorted = [f(a, 1), f(a, 1), f(b, 2), f(a, 2)].
Making the assumption that for every pair of A
and B
passed to the comparison predicate Pred(Delta, A, B)
, A
comes before B
in the original list. Can we define a comparison:
compare_argn_stable(N, Delta, A, B) :-
arg(N, A, AN),
arg(N, B, BN),
compare(Delta0, AN, BN),
( Delta0 == (=)
-> Delta = (<)
; Delta = Delta0
).
At this point, if and only if any two elements A
and B
are always passed to the comparison predicate in the same order as they were in the original list, this should behave identically to sort_argn/3
above:
?- predsort(compare_argn_stable(N), List, Sorted).
Now of course it is important that compare_argn_stable/4
unifies Delta
with <
when the two "keys" are equal. Furthermore, the behavior is implementation dependent, and only identical to the keysort
example iff predsort/3
keeps the original order of elements when passing them to the comparison predicate.
Question Is that correct?
Question Is there any standard that covers this aspect of predsort/3
?