std.algorithm: {min,max}Element for a single range

This commit is contained in:
Sebastian Wilzbach 2016-02-25 12:11:01 +02:00
parent d2740c4498
commit e3d0aa9724
3 changed files with 292 additions and 0 deletions

View file

@ -1270,6 +1270,9 @@ Params:
Returns: Returns:
The maximum of the passed-in args. The type of the returned value is The maximum of the passed-in args. The type of the returned value is
the type among the passed arguments that is able to store the largest value. the type among the passed arguments that is able to store the largest value.
See_Also:
$(XREF_PACK algorithm,searching,maxElement)
*/ */
MaxType!T max(T...)(T args) MaxType!T max(T...)(T args)
if (T.length >= 2) if (T.length >= 2)
@ -1382,6 +1385,8 @@ Iterates the passed arguments and returns the minimum value.
Params: args = The values to select the minimum from. At least two arguments Params: args = The values to select the minimum from. At least two arguments
must be passed, and they must be comparable with `<`. must be passed, and they must be comparable with `<`.
Returns: The minimum of the passed-in values. Returns: The minimum of the passed-in values.
See_Also:
$(XREF_PACK algorithm,searching,minElement)
*/ */
MinType!T min(T...)(T args) MinType!T min(T...)(T args)
if (T.length >= 2) if (T.length >= 2)

View file

@ -35,6 +35,8 @@ $(TR $(TDNW Searching)
$(SUBREF searching, findSplitBefore) $(SUBREF searching, findSplitBefore)
$(SUBREF searching, minCount) $(SUBREF searching, minCount)
$(SUBREF searching, maxCount) $(SUBREF searching, maxCount)
$(SUBREF searching, minElement)
$(SUBREF searching, maxElement)
$(SUBREF searching, minPos) $(SUBREF searching, minPos)
$(SUBREF searching, maxPos) $(SUBREF searching, maxPos)
$(SUBREF searching, skipOver) $(SUBREF searching, skipOver)

View file

@ -59,6 +59,12 @@ $(T2 minCount,
$(D minCount([2, 1, 1, 4, 1])) returns $(D tuple(1, 3)).) $(D minCount([2, 1, 1, 4, 1])) returns $(D tuple(1, 3)).)
$(T2 maxCount, $(T2 maxCount,
$(D maxCount([2, 4, 1, 4, 1])) returns $(D tuple(4, 2)).) $(D maxCount([2, 4, 1, 4, 1])) returns $(D tuple(4, 2)).)
$(T2 minElement,
Selects the minimal element of a range.
`minElement([3, 4, 1, 2])` returns `1`.)
$(T2 maxElement,
Selects the maximal element of a range.
`maxElement([3, 4, 1, 2])` returns `4`.)
$(T2 minPos, $(T2 minPos,
$(D minPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [1, 3, 4, 1]), $(D minPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [1, 3, 4, 1]),
i.e., positions the range at the first occurrence of its minimal i.e., positions the range at the first occurrence of its minimal
@ -1204,6 +1210,103 @@ bool endsWith(alias pred, R)(R doesThisEnd)
} }
} }
/**
Iterates the passed range and selects the extreme element with `less`.
If the extreme element occurs multiple time, the first occurrence will be
returned.
Params:
map = custom accessor for the comparison key
selector = custom mapping for the extrema selection
seed = custom seed to use as initial element
r = Range from which the extreme value will be selected
Returns:
The extreme value according to `map` and `selector` of the passed-in values.
*/
private auto extremum(alias map = "a", alias selector = "a < b", Range)(Range r)
if (isInputRange!Range && !isInfinite!Range)
in
{
assert(!r.empty, "r is an empty range");
}
body
{
alias mapFun = unaryFun!map;
alias Element = ElementType!Range;
Unqual!Element seed = r.front;
r.popFront();
return extremum!(map, selector)(r, seed);
}
private auto extremum(alias map = "a", alias selector = "a < b", Range,
RangeElementType = ElementType!Range)
(Range r, RangeElementType seedElement)
if (isInputRange!Range && !isInfinite!Range &&
!is(CommonType!(ElementType!Range, RangeElementType) == void))
{
alias mapFun = unaryFun!map;
alias selectorFun = binaryFun!selector;
alias Element = ElementType!Range;
alias CommonElement = CommonType!(Element, RangeElementType);
alias MapType = Unqual!(typeof(mapFun(CommonElement.init)));
Unqual!CommonElement extremeElement = seedElement;
MapType extremeElementMapped = mapFun(extremeElement);
static if (isRandomAccessRange!Range && hasLength!Range)
{
foreach (const i; 0 .. r.length)
{
MapType mapElement = mapFun(r[i]);
if (selectorFun(mapElement, extremeElementMapped))
{
extremeElement = r[i];
extremeElementMapped = mapElement;
}
}
}
else
{
while (!r.empty)
{
MapType mapElement = mapFun(r.front);
if (selectorFun(mapElement, extremeElementMapped))
{
extremeElement = r.front;
extremeElementMapped = mapElement;
}
r.popFront();
}
}
return extremeElement;
}
@safe pure nothrow unittest
{
// allows a custom map to select the extremum
assert([[0, 4], [1, 2]].extremum!"a[0]" == [0, 4]);
assert([[0, 4], [1, 2]].extremum!"a[1]" == [1, 2]);
// allows a custom selector for comparison
assert([[0, 4], [1, 2]].extremum!("a[0]", "a > b") == [1, 2]);
assert([[0, 4], [1, 2]].extremum!("a[1]", "a > b") == [0, 4]);
}
@safe pure nothrow unittest
{
// allow seeds
int[] arr;
assert(arr.extremum(1) == 1);
int[][] arr2d;
assert(arr2d.extremum([1]) == [1]);
// allow seeds of different types (implicit casting)
assert([2, 3, 4].extremum(1.5) == 1.5);
}
// find // find
/** /**
Finds an individual element in an input range. Elements of $(D Finds an individual element in an input range. Elements of $(D
@ -2960,6 +3063,188 @@ unittest
} }
} }
/**
Iterates the passed range and returns the minimal element.
A custom mapping function can be passed to `map`.
Complexity: O(n)
Exactly `n - 1` comparisons are needed.
Params:
map = custom accessor for the comparison key
r = range from which the minimal element will be selected
seed = custom seed to use as initial element
Returns: The minimal element of the passed-in range.
See_Also:
$(XREF algorithm, comparison, min)
*/
auto minElement(alias map = "a", Range)(Range r)
if (isInputRange!Range && !isInfinite!Range)
{
return extremum!map(r);
}
/// ditto
auto minElement(alias map = "a", Range, RangeElementType = ElementType!Range)
(Range r, RangeElementType seed)
if (isInputRange!Range && !isInfinite!Range &&
!is(CommonType!(ElementType!Range, RangeElementType) == void))
{
return extremum!map(r, seed);
}
///
@safe pure unittest
{
import std.range: enumerate;
assert([2, 1, 4, 3].minElement == 1);
// allows to get the index of an element too
assert([5, 3, 7, 9].enumerate.minElement!"a.value" == tuple(1, 3));
// any custom accessor can be passed
assert([[0, 4], [1, 2]].minElement!"a[1]" == [1, 2]);
// can be seeded
int[] arr;
assert(arr.minElement(1) == 1);
}
@safe pure unittest
{
import std.range: enumerate, iota;
// supports mapping
assert([3, 4, 5, 1, 2].enumerate.minElement!"a.value" == tuple(3, 1));
assert([5, 2, 4].enumerate.minElement!"a.value" == tuple(1, 2));
// forward ranges
assert(iota(1, 5).minElement() == 1);
assert(iota(2, 5).enumerate.minElement!"a.value" == tuple(0, 2));
// should work with const
const(int)[] immArr = [2, 1, 3];
assert(immArr.minElement == 1);
// should work with immutable
immutable(int)[] immArr2 = [2, 1, 3];
assert(immArr2.minElement == 1);
// with strings
assert(["b", "a", "c"].minElement == "a");
// with all dummy ranges
import std.internal.test.dummyrange;
foreach (DummyType; AllDummyRanges)
{
DummyType d;
assert(d.minElement == 1);
}
}
@nogc @safe nothrow pure unittest
{
static immutable arr = [7, 3, 4, 2, 1, 8];
assert(arr.minElement == 1);
static immutable arr2d = [[1, 9], [3, 1], [4, 2]];
assert(arr2d.minElement!"a[1]" == arr2d[1]);
}
/**
Iterates the passed range and returns the maximal element.
A custom mapping function can be passed to `map`.
Complexity:
Exactly `n - 1` comparisons are needed.
Params:
map = custom accessor for the comparison key
r = range from which the maximum will be selected
seed = custom seed to use as initial element
Returns: The maximal element of the passed-in range.
See_Also:
$(XREF algorithm, comparison, max)
*/
auto maxElement(alias map = "a", Range)(Range r)
if (isInputRange!Range && !isInfinite!Range &&
!is(CommonType!(ElementType!Range, RangeElementType) == void))
{
return extremum!(map, "a > b")(r);
}
/// ditto
auto maxElement(alias map = "a", Range, RangeElementType = ElementType!Range)
(Range r, RangeElementType seed)
if (isInputRange!Range && !isInfinite!Range)
{
return extremum!(map, "a > b")(r, seed);
}
///
@safe pure unittest
{
import std.range: enumerate;
assert([2, 1, 4, 3].maxElement == 4);
// allows to get the index of an element too
assert([2, 1, 4, 3].enumerate.maxElement!"a.value" == tuple(2, 4));
// any custom accessor can be passed
assert([[0, 4], [1, 2]].maxElement!"a[1]" == [0, 4]);
// can be seeded
int[] arr;
assert(arr.minElement(1) == 1);
}
@safe pure unittest
{
import std.range: enumerate, iota;
// supports mapping
assert([3, 4, 5, 1, 2].enumerate.maxElement!"a.value" == tuple(2, 5));
assert([5, 2, 4].enumerate.maxElement!"a.value" == tuple(0, 5));
// forward ranges
assert(iota(1, 5).maxElement() == 4);
assert(iota(2, 5).enumerate.maxElement!"a.value" == tuple(2, 4));
assert(iota(4, 14).enumerate.maxElement!"a.value" == tuple(9, 13));
// should work with const
const(int)[] immArr = [2, 3, 1];
assert(immArr.maxElement == 3);
// should work with immutable
immutable(int)[] immArr2 = [2, 3, 1];
assert(immArr2.maxElement == 3);
// with strings
assert(["a", "c", "b"].maxElement == "c");
// with all dummy ranges
import std.internal.test.dummyrange;
foreach (DummyType; AllDummyRanges)
{
DummyType d;
assert(d.maxElement == 10);
}
}
@nogc @safe nothrow pure unittest
{
static immutable arr = [7, 3, 8, 2, 1, 4];
assert(arr.maxElement == 8);
static immutable arr2d = [[1, 3], [3, 9], [4, 2]];
assert(arr2d.maxElement!"a[1]" == arr2d[1]);
}
// minPos // minPos
/** /**
Computes a subrange of `range` starting at the first occurrence of `range`'s Computes a subrange of `range` starting at the first occurrence of `range`'s