Merge pull request #3942 from andralex/maxPos

Add maxCount and maxPos
This commit is contained in:
H. S. Teoh 2016-01-27 21:18:43 -08:00
commit 02c0083e1c
2 changed files with 80 additions and 19 deletions

View file

@ -57,10 +57,16 @@ $(T2 findSplitBefore,
and $(D "defg").)
$(T2 minCount,
$(D minCount([2, 1, 1, 4, 1])) returns $(D tuple(1, 3)).)
$(T2 maxCount,
$(D maxCount([2, 4, 1, 4, 1])) returns $(D tuple(4, 2)).)
$(T2 minPos,
$(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
element.)
$(T2 maxPos,
$(D maxPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [4, 1]),
i.e., positions the range at the first occurrence of its maximal
element.)
$(T2 mismatch,
$(D mismatch("parakeet", "parachute")) returns the two ranges
$(D "keet") and $(D "chute").)
@ -2718,20 +2724,37 @@ if (isForwardRange!R1 && isForwardRange!R2)
assert(equal(r2[1], a[4 .. $]));
}
// minCount
/**
Computes the minimum (respectively maximum) of `range` along with its number of
occurrences. Formally, the minimum is a value `x` in `range` such that $(D
pred(a, x)) is `false` for all values `a` in `range`. Conversely, the maximum is
a value `x` in `range` such that $(D pred(x, a)) is `false` for all values `a`
in `range` (note the swapped arguments to `pred`).
These functions may be used for computing arbitrary extrema by choosing `pred`
appropriately. For corrrect functioning, `pred` must be a strict partial order,
i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and
irreflexive ($(D pred(a, a)) is `false`). The $(LUCKY trichotomy property of
inequality) is not required: these algoritms consider elements `a` and `b` equal
(for the purpose of counting) if `pred` puts them in the same equivalence class,
i.e. $(D !pred(a, b) && !pred(b, a)).
Params:
pred = The ordering predicate to use to determine the minimal element.
pred = The ordering predicate to use to determine the extremum (minimum
or maximum).
range = The input range to count.
Returns: The minimum element of a range together with the number of
occurrences. The function can actually be used for counting the
maximum or any other ordering predicate (that's why $(D maxCount) is
not provided).
Returns: The minimum, respectively maximum element of a range together with the
number it occurs in the range.
Throws: `Exception` if `range.empty`.
*/
Tuple!(ElementType!Range, size_t)
minCount(alias pred = "a < b", Range)(Range range)
if (isInputRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
if (isInputRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
{
import std.algorithm.internal : algoFormat;
import std.exception : enforce;
@ -2752,7 +2775,12 @@ minCount(alias pred = "a < b", Range)(Range range)
Range least = range.save;
for (range.popFront(); !range.empty; range.popFront())
{
if (binaryFun!pred(least.front, range.front)) continue;
if (binaryFun!pred(least.front, range.front))
{
assert(!binaryFun!pred(range.front, least.front),
"min/maxPos: predicate must be a strict partial order.");
continue;
}
if (binaryFun!pred(range.front, least.front))
{
// change the min
@ -2809,6 +2837,15 @@ minCount(alias pred = "a < b", Range)(Range range)
"to keep track of the smallest %s element.", Range.stringof, T.stringof));
}
/// Ditto
Tuple!(ElementType!Range, size_t)
maxCount(alias pred = "a < b", Range)(Range range)
if (isInputRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
{
return range.minCount!((a, b) => binaryFun!pred(b, a));
}
///
unittest
{
@ -2819,9 +2856,9 @@ unittest
int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
// Minimum is 1 and occurs 3 times
assert(minCount(a) == tuple(1, 3));
assert(a.minCount == tuple(1, 3));
// Maximum is 4 and occurs 2 times
assert(minCount!("a > b")(a) == tuple(4, 2));
assert(a.maxCount == tuple(4, 2));
}
unittest
@ -2913,16 +2950,30 @@ unittest
// minPos
/**
Computes a subrange of `range` starting at the first occurrence of `range`'s
minimum (respectively maximum) and with the same ending as `range`, or the
empty range if `range` itself is empty.
Formally, the minimum is a value `x` in `range` such that $(D pred(a, x)) is
`false` for all values `a` in `range`. Conversely, the maximum is a value `x` in
`range` such that $(D pred(x, a)) is `false` for all values `a` in `range` (note
the swapped arguments to `pred`).
These functions may be used for computing arbitrary extrema by choosing `pred`
appropriately. For corrrect functioning, `pred` must be a strict partial order,
i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and
irreflexive ($(D pred(a, a)) is `false`).
Params:
pred = The ordering predicate to use to determine the minimal element.
pred = The ordering predicate to use to determine the extremum (minimum or
maximum) element.
range = The input range to search.
Returns: The position of the minimum element of forward range $(D range), i.e.
a subrange of $(D range) starting at the position of its smallest element and
with the same ending as $(D range). The function can actually be used for
finding the maximum or any other ordering predicate (that's why $(D maxPos) is
not provided).
*/
Returns: The position of the minimum (respectively maximum) element of forward
range `range`, i.e. a subrange of `range` starting at the position of its
smallest (respectively largest) element and with the same ending as `range`.
*/
Range minPos(alias pred = "a < b", Range)(Range range)
if (isForwardRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
@ -2958,14 +3009,22 @@ Range minPos(alias pred = "a < b", Range)(Range range)
}
}
/// Ditto
Range maxPos(alias pred = "a < b", Range)(Range range)
if (isForwardRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
{
return range.minPos!((a, b) => binaryFun!pred(b, a));
}
///
@safe unittest
{
int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
// Minimum is 1 and first occurs in position 3
assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]);
assert(a.minPos == [ 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 ]);
assert(a.maxPos == [ 4, 1, 2, 4, 1, 1, 2 ]);
}
@safe unittest