mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 22:50:38 +03:00
std.algorithm: {min,max}Element for a single range
This commit is contained in:
parent
d2740c4498
commit
e3d0aa9724
3 changed files with 292 additions and 0 deletions
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue