Merge pull request #4235 from wilzbach/multisort_with_pred

Fix issue 10777 - multiSort should return a SortedRange
This commit is contained in:
Martin Nowak 2016-05-06 00:43:56 +02:00
commit 72fdea91e0

View file

@ -847,7 +847,7 @@ private template validPredicates(E, less...)
} }
/** /**
$(D void multiSort(Range)(Range r) $(D auto multiSort(Range)(Range r)
if (validPredicates!(ElementType!Range, less));) if (validPredicates!(ElementType!Range, less));)
Sorts a range by multiple keys. The call $(D multiSort!("a.id < b.id", Sorts a range by multiple keys. The call $(D multiSort!("a.id < b.id",
@ -856,12 +856,18 @@ and sorts elements that have the same $(D id) by $(D date)
descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id
< b.id : a.date > b.date"(r)), but $(D multiSort) is faster because it < b.id : a.date > b.date"(r)), but $(D multiSort) is faster because it
does fewer comparisons (in addition to being more convenient). does fewer comparisons (in addition to being more convenient).
Returns:
The initial range wrapped as a $(D SortedRange) with its predicates
converted to an equivalent single predicate.
*/ */
template multiSort(less...) //if (less.length > 1) template multiSort(less...) //if (less.length > 1)
{ {
void multiSort(Range)(Range r) auto multiSort(Range)(Range r)
if (validPredicates!(ElementType!Range, less)) if (validPredicates!(ElementType!Range, less))
{ {
import std.range : assumeSorted;
import std.meta: AliasSeq;
static if (is(typeof(less[$ - 1]) == SwapStrategy)) static if (is(typeof(less[$ - 1]) == SwapStrategy))
{ {
enum ss = less[$ - 1]; enum ss = less[$ - 1];
@ -872,6 +878,16 @@ template multiSort(less...) //if (less.length > 1)
alias ss = SwapStrategy.unstable; alias ss = SwapStrategy.unstable;
alias funs = less; alias funs = less;
} }
static if (funs.length == 0)
assert("No sorting predicate provided");
else
static if (funs.length == 1)
return sort!(funs[0], ss, Range)(r);
else
{
static void multiSortImpl(funs...)(Range r) @safe
{
alias lessFun = binaryFun!(funs[0]); alias lessFun = binaryFun!(funs[0]);
static if (funs.length > 1) static if (funs.length > 1)
@ -879,17 +895,17 @@ template multiSort(less...) //if (less.length > 1)
while (r.length > 1) while (r.length > 1)
{ {
auto p = getPivot!lessFun(r); auto p = getPivot!lessFun(r);
auto t = partition3!(less[0], ss)(r, r[p]); auto t = partition3!(funs[0], ss)(r, r[p]);
if (t[0].length <= t[2].length) if (t[0].length <= t[2].length)
{ {
.multiSort!less(t[0]); multiSortImpl!funs(t[0]);
.multiSort!(less[1 .. $])(t[1]); multiSortImpl!(funs[1 .. $])(t[1]);
r = t[2]; r = t[2];
} }
else else
{ {
.multiSort!(less[1 .. $])(t[1]); multiSortImpl!(funs[1 .. $])(t[1]);
.multiSort!less(t[2]); multiSortImpl!funs(t[2]);
r = t[0]; r = t[0];
} }
} }
@ -899,6 +915,21 @@ template multiSort(less...) //if (less.length > 1)
sort!(lessFun, ss)(r); sort!(lessFun, ss)(r);
} }
} }
static bool predFun(ElementType!Range a, ElementType!Range b)
{
foreach (f; AliasSeq!(funs))
{
alias lessFun = binaryFun!(f);
if (lessFun(a, b)) return true;
if (lessFun(b, a)) return false;
}
return false;
}
multiSortImpl!funs(r);
return assumeSorted!predFun(r);
}
}
} }
/// ///
@ -924,8 +955,10 @@ template multiSort(less...) //if (less.length > 1)
assert(pts1 == pts2); assert(pts1 == pts2);
auto pts3 = indexed(pts1, iota(pts1.length)); auto pts3 = indexed(pts1, iota(pts1.length));
multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts3); assert(pts3.multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable).release.equal(pts2));
assert(equal(pts3, pts2));
auto pts4 = iota(10).array;
assert(pts4.multiSort!("a > b").release.equal(iota(10).retro));
} }
@safe unittest //issue 9160 (L-value only comparators) @safe unittest //issue 9160 (L-value only comparators)