diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index fcb9b4fa6e1..ed43ad1cb4a 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2781,19 +2781,26 @@ Range minPos(alias pred = "a < b", Range)(Range range) if (isForwardRange!Range && !isInfinite!Range && is(typeof(binaryFun!pred(range.front, range.front)))) { - if (range.empty) return range; - auto result = range.save; - - for (range.popFront(); !range.empty; range.popFront()) + static if (isSortedRange!(Range, pred)) + { + return range; + } + else { - //Note: Unlike minCount, we do not care to find equivalence, so a single pred call is enough - if (binaryFun!pred(range.front, result.front)) + if (range.empty) return range; + auto result = range.save; + + for (range.popFront(); !range.empty; range.popFront()) { - // change the min - result = range.save; + //Note: Unlike minCount, we do not care to find equivalence, so a single pred call is enough + if (binaryFun!pred(range.front, result.front)) + { + // change the min + result = range.save; + } } + return result; } - return result; } /// @@ -2804,6 +2811,11 @@ Range minPos(alias pred = "a < b", Range)(Range range) assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]); // Maximum is 4 and first occurs in position 2 assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]); + + // Test SortedRange as input + import std.algorithm.sorting : sort; + import std.algorithm : equal; + assert(equal(minPos(a.sort()), [ 1, 1, 1, 2, 2, 2, 3, 4, 4 ])); } @safe unittest @@ -2852,6 +2864,76 @@ unittest assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]); } +import std.algorithm.comparison : min, max; + +/** Returns: Minimum Element in $(D range) or $(D unit) if $(D range) is empty. + */ +auto minElement(alias F = min, R)(R range, + ElementType!R unit = ElementType!R.max) + if (isInputRange!R) +{ + import std.range.primitives : isSortedRange; + static if (isSortedRange!(R, "a < b")) + { + import std.range.primitives : front; + return range.empty ? unit : range.front; + } + else static if (isSortedRange!(R, "a > b") && + isBidirectionalRange!R) + { + import std.range.primitives : back; + return range.empty ? unit : range.back; + } + else + { + import std.algorithm.iteration : reduce; + return reduce!F(unit, range); + } +} + +@safe pure nothrow unittest +{ + import std.algorithm.sorting : sort, assumeSorted; + auto x = [2, 4, 1, 3]; + assert(x.minElement == 1); + assert(x.sort!"a < b".minElement == 1); + assert(x.sort!"a > b".minElement == 1); +} + +/** Returns: Maximum Element in $(D range) or $(D unit) if $(D range) is empty. + */ +auto maxElement(alias F = max, R)(R range, + ElementType!R unit = ElementType!R.min) + if (isInputRange!R) +{ + import std.range.primitives : isSortedRange; + static if (isSortedRange!(R, "a > b")) + { + import std.range.primitives : front; + return range.empty ? unit : range.front; + } + else static if (isSortedRange!(R, "a < b") && + isBidirectionalRange!R) + { + import std.range.primitives : back; + return range.empty ? unit : range.back; + } + else + { + import std.algorithm.iteration : reduce; + return reduce!F(unit, range); + } +} + +@safe pure nothrow unittest +{ + import std.algorithm.sorting : sort, assumeSorted; + auto x = [2, 4, 1, 3]; + assert(x.maxElement == 4); + assert(x.sort!"a < b".maxElement == 4); + assert(x.sort!"a > b".maxElement == 4); +} + /** Skip over the initial portion of the first given range that matches the second range, or do nothing if there is no match. @@ -3494,4 +3576,3 @@ unittest // Issue 13124 auto s = "hello how\nare you"; s.until!(c => c.among!('\n', '\r')); } - diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 7012d01eb06..c2c46fb958b 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -131,39 +131,45 @@ less). */ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) { - if (r.empty) return true; - - static if (isRandomAccessRange!Range && hasLength!Range) + static if (isSortedRange!(Range, less)) { - immutable limit = r.length - 1; - foreach (i; 0 .. limit) - { - if (!binaryFun!less(r[i + 1], r[i])) continue; - assert( - !binaryFun!less(r[i], r[i + 1]), - "Predicate for isSorted is not antisymmetric. Both" ~ - " pred(a, b) and pred(b, a) are true for certain values."); - return false; - } + return true; } else { - auto ahead = r; - ahead.popFront(); - size_t i; - - for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i) + if (r.empty) return true; + static if (isRandomAccessRange!Range && hasLength!Range) { - if (!binaryFun!less(ahead.front, r.front)) continue; - // Check for antisymmetric predicate - assert( - !binaryFun!less(r.front, ahead.front), - "Predicate for isSorted is not antisymmetric. Both" ~ - " pred(a, b) and pred(b, a) are true for certain values."); - return false; + immutable limit = r.length - 1; + foreach (i; 0 .. limit) + { + if (!binaryFun!less(r[i + 1], r[i])) continue; + assert( + !binaryFun!less(r[i], r[i + 1]), + "Predicate for isSorted is not antisymmetric. Both" ~ + " pred(a, b) and pred(b, a) are true for certain values."); + return false; + } } + else + { + auto ahead = r; + ahead.popFront(); + size_t i; + + for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i) + { + if (!binaryFun!less(ahead.front, r.front)) continue; + // Check for antisymmetric predicate + assert( + !binaryFun!less(r.front, ahead.front), + "Predicate for isSorted is not antisymmetric. Both" ~ + " pred(a, b) and pred(b, a) are true for certain values."); + return false; + } + } + return true; } - return true; } /// @@ -175,6 +181,7 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) assert(isSorted(arr)); sort!("a > b")(arr); assert(isSorted!("a > b")(arr)); + assert(isSorted(sort(arr))); } @safe unittest @@ -954,7 +961,7 @@ See_Also: */ SortedRange!(Range, less) sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Range)(Range r) + Range)(Range r) if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || hasAssignableElements!Range)) || (ss != SwapStrategy.unstable && hasAssignableElements!Range)) && @@ -966,25 +973,32 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, swaps using assignment. Stable sorting uses TimSort, which needs to copy elements into a buffer, requiring assignable elements. +/ -{ - import std.range : assumeSorted; - alias lessFun = binaryFun!(less); - alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun - static if (is(LessRet == bool)) { - static if (ss == SwapStrategy.unstable) - quickSortImpl!(lessFun)(r, r.length); - else //use Tim Sort for semistable & stable - TimSortImpl!(lessFun, Range).sort(r, null); - - enum maxLen = 8; - assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); + static if (isSortedRange!(Range, less)) + { + return r; // already sorted, so just return $(D r) as is } else { - static assert(false, "Invalid predicate passed to sort: " ~ less.stringof); + import std.range : assumeSorted; + alias lessFun = binaryFun!(less); + alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun + static if (is(LessRet == bool)) + { + static if (ss == SwapStrategy.unstable) + quickSortImpl!(lessFun)(r, r.length); + else //use Tim Sort for semistable & stable + TimSortImpl!(lessFun, Range).sort(r, null); + + enum maxLen = 8; + assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); + } + else + { + static assert(false, "Invalid predicate passed to sort: " ~ less.stringof); + } + return assumeSorted!less(r); } - return assumeSorted!less(r); } /// @@ -2859,4 +2873,3 @@ shapes. Here's a non-trivial example: } assert(n == 60); } - diff --git a/std/range/primitives.d b/std/range/primitives.d index aff94f9b2d6..43ce0ae63f4 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -31,6 +31,10 @@ $(BOOKTABLE , bidirectional _range that also supports the array subscripting operation via the primitive $(D opIndex). )) + $(TR $(TD $(D $(LREF isSortedRange))) + $(TD Tests if something is a $(I sorted _range) sorted on a specific + predicate function $(D pred). + )) ) It also provides number of templates that test for various _range capabilities: @@ -993,6 +997,145 @@ unittest static assert( isRandomAccessRange!(inout(int)[])); } +auto standardizePredicatePrefix(S)(S s) if (isSomeString!S) +{ + if (s.length >= 2) + { + if (s[0] != 'a') + return s; + + size_t n = 0; // whitespace count + while (1 + n < s.length && + s[1 + n] == ' ') + ++n; + + return 'a' ~ s[1 + n .. $]; + } + return s; +} + +auto standardizePredicateSuffix(S)(S s) if (isSomeString!S) +{ + if (s.length >= 2) + { + if (s[$ - 1] != 'b') + return s; + + size_t n = 0; // whitespace count + while (1 + n < s.length && + s[$ - 2 - n] == ' ') + ++n; + + return s[0 .. $ - 1 - n] ~ 'b'; + } + return s; +} + +auto standardizePredicate(S)(S s) if (isSomeString!S) +{ + return standardizePredicateSuffix(standardizePredicatePrefix(s)); +} + +unittest +{ + static assert(standardizePredicate("a < b") == "a b") == "a>b"); +} + +import std.functional : binaryFun; + +enum binaryFunString(alias pred : binaryFun!T, T ...) = T[0]; + +unittest +{ + enum pred = "a+b"; + alias x = binaryFun!pred; + static assert(binaryFunString!x == pred); +} + +/** + Returns true if $(D T) is a Range Sorted on predicate $(D pred). + + Currently checks if $(D T) is an instance of $(D SortedRange). + + See also: http://forum.dlang.org/post/mqskge$2968$1@digitalmars.com +*/ +template isSortedRange(T, alias pred = "a < b") +{ + import std.range : SortedRange; + static if (is(T == SortedRange!Args, Args...)) + { + import std.traits : TemplateArgsOf; + alias TArgs = TemplateArgsOf!T; + static if (TArgs.length == 2) + { + alias predArg = TArgs[1]; + static if (isSomeString!(typeof(pred))) + { + alias predString = pred; + } + else + { + alias predString = binaryFunString!pred; + } + + static if (isSomeString!(typeof(predArg))) + { + alias predArgString = predArg; + } + else + { + alias predArgString = binaryFunString!predArg; + } + + enum isSortedRange = (standardizePredicate(predString) == + standardizePredicate(predArgString)); + } + else + { + enum isSortedRange = false; + } + } + else + { + enum isSortedRange = false; + } +} + +/// +unittest +{ + import std.functional : binaryFun; + + alias R = int[]; + + enum pred = "a < b"; + alias fun = binaryFun!"a b"; + + import std.range : SortedRange; + + alias SR = SortedRange!(R, pred); + static assert(isSortedRange!(SR, pred)); + static assert(isSortedRange!(SR, fun)); + + alias SR2 = SortedRange!(R, binaryFun!pred); + static assert(isSortedRange!(SR2, pred)); + static assert(isSortedRange!(SR2, fun)); + + alias SR_ = SortedRange!(R, pred); + static assert(!isSortedRange!(SR_, rpred)); + static assert(!isSortedRange!(SR_, rfun)); + + alias IR = int[]; + static assert(!isSortedRange!(IR, pred)); + static assert(!isSortedRange!(IR, fun)); +} + @safe unittest { // Test fix for bug 6935. diff --git a/std/traits.d b/std/traits.d index a9ddb1af446..2eb53cc9706 100644 --- a/std/traits.d +++ b/std/traits.d @@ -5544,7 +5544,7 @@ unittest } /** - * Returns true if T is an instance of the template S. + Returns true if $(D T) is an instance of the template $(D T). */ enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...);