问题
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
?
回答1:
Since no one has answered, and since I am quite certain about it now:
Yes, you could use predsort/3
to emulate any of the other sorts. The question describes in some detail how.
However: this is a bad idea for several reasons.
- The "stability" depends on the implementation of
predsort/3
(see the question) - The
predsort/3
itself is not part of any standard (as far as I can tell) - The chances are, your Prolog implementation provides an
msort/2
orkeysort/2
that is far more efficient thanpredsort/3
There might be rare cases where the size of the elements of the list is much bigger than the length of the list we are sorting, and this little dance:
list_to_keyval_pairs(List, Pairs), % defined by the user as necessary
keysort(Pairs, Sorted_pairs),
pairs_values(Sorted_pairs, Sorted)
(see here) is actually more expensive (slower) than using predsort(keycmp, List, Sorted)
, with keycmp/3
defined by the user. Even then, the order of results with equivalent keys depends not only on the (user) definition of keycmp/3
, but also on the implementation of predsort/3
.
In other words, a "stable" sort with predsort/3
is a bad idea.
来源:https://stackoverflow.com/questions/28076715/possible-behaviors-of-predsort-3