Skip to content

Commit d2c7d37

Browse files
committed
Issue 8573 - A simpler Phobos function that returns the index of the mix or max item
Issue 8573 - A simpler Phobos function that returns the index of the mix or max item added some review fixes fixed an issue with a mutable variable Applied review feedback Renamed functions to minIndex and maxIndex + used sizediff_t for return value type Updated function so that it works optimally even for lazy ranges and algorithms Reverted to having only copyable elements in ranges Added more unittests; implemented an array path; fixed documentation Squashed commits
1 parent 72af009 commit d2c7d37

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

std/algorithm/searching.d

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3544,6 +3544,213 @@ unittest
35443544
assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]);
35453545
}
35463546

3547+
/**
3548+
Computes the index of the first occurrence of `range`'s minimum element.
3549+
3550+
Params:
3551+
pred = The ordering predicate to use to determine the minimum element.
3552+
range = The input range to search.
3553+
3554+
Complexity: O(n)
3555+
Exactly `n - 1` comparisons are needed.
3556+
3557+
Returns:
3558+
The index of the first encounter of the minimum element in `range`. If the
3559+
`range` is empty, -1 is returned.
3560+
3561+
See_Also:
3562+
$(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos)
3563+
*/
3564+
sizediff_t minIndex(alias pred = "a < b", Range)(Range range)
3565+
if (isForwardRange!Range && !isInfinite!Range &&
3566+
is(typeof(binaryFun!pred(range.front, range.front))))
3567+
{
3568+
if (range.empty) return -1;
3569+
3570+
sizediff_t minPos = 0;
3571+
3572+
static if (isRandomAccessRange!Range && hasLength!Range)
3573+
{
3574+
foreach (i; 1 .. range.length)
3575+
{
3576+
if (binaryFun!pred(range[i], range[minPos]))
3577+
{
3578+
minPos = i;
3579+
}
3580+
}
3581+
}
3582+
else
3583+
{
3584+
sizediff_t curPos = 0;
3585+
Unqual!(typeof(range.front)) min = range.front;
3586+
for (range.popFront(); !range.empty; range.popFront())
3587+
{
3588+
++curPos;
3589+
if (binaryFun!pred(range.front, min))
3590+
{
3591+
min = range.front;
3592+
minPos = curPos;
3593+
}
3594+
}
3595+
}
3596+
return minPos;
3597+
}
3598+
3599+
///
3600+
@safe pure nothrow unittest
3601+
{
3602+
int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2];
3603+
3604+
// Minimum is 1 and first occurs in position 3
3605+
assert(a.minIndex == 3);
3606+
// Get maximum index with minIndex
3607+
assert(a.minIndex!"a > b" == 2);
3608+
3609+
// Range is empty, so return value is -1
3610+
int[] b;
3611+
assert(b.minIndex == -1);
3612+
3613+
// Works with more custom types
3614+
struct Dog { int age; }
3615+
Dog[] dogs = [Dog(10), Dog(5), Dog(15)];
3616+
assert(dogs.minIndex!"a.age < b.age" == 1);
3617+
}
3618+
3619+
@safe pure unittest
3620+
{
3621+
// should work with const
3622+
const(int)[] immArr = [2, 1, 3];
3623+
assert(immArr.minIndex == 1);
3624+
3625+
// Works for const ranges too
3626+
const int[] c = [2, 5, 4, 1, 2, 3];
3627+
assert(c.minIndex == 3);
3628+
3629+
// should work with immutable
3630+
immutable(int)[] immArr2 = [2, 1, 3];
3631+
assert(immArr2.minIndex == 1);
3632+
3633+
// with strings
3634+
assert(["b", "a", "c"].minIndex == 1);
3635+
3636+
// infinite range
3637+
import std.range : cycle;
3638+
static assert(!__traits(compiles, cycle([1]).minIndex));
3639+
3640+
// with all dummy ranges
3641+
import std.internal.test.dummyrange : AllDummyRanges;
3642+
foreach (DummyType; AllDummyRanges)
3643+
{
3644+
static if (isForwardRange!DummyType && !isInfinite!DummyType)
3645+
{
3646+
DummyType d;
3647+
d.arr = [5, 3, 7, 2, 1, 4];
3648+
assert(d.minIndex == 4);
3649+
3650+
d.arr = [];
3651+
assert(d.minIndex == -1);
3652+
}
3653+
}
3654+
}
3655+
3656+
@nogc @safe nothrow pure unittest
3657+
{
3658+
static immutable arr = [7, 3, 8, 2, 1, 4];
3659+
assert(arr.minIndex == 4);
3660+
3661+
static immutable arr2d = [[1, 3], [3, 9], [4, 2]];
3662+
assert(arr2d.minIndex!"a[1] < b[1]" == 2);
3663+
}
3664+
3665+
/**
3666+
Computes the index of the first occurrence of `range`'s maximum element.
3667+
3668+
Complexity: O(n)
3669+
Exactly `n - 1` comparisons are needed.
3670+
3671+
Params:
3672+
pred = The ordering predicate to use to determine the maximum element.
3673+
range = The input range to search.
3674+
3675+
Returns:
3676+
The index of the first encounter of the maximum in `range`. If the
3677+
`range` is empty, -1 is returned.
3678+
3679+
See_Also:
3680+
$(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos)
3681+
*/
3682+
sizediff_t maxIndex(alias pred = "a < b", Range)(Range range)
3683+
if (isInputRange!Range && !isInfinite!Range &&
3684+
is(typeof(binaryFun!pred(range.front, range.front))))
3685+
{
3686+
return range.minIndex!((a, b) => binaryFun!pred(b, a));
3687+
}
3688+
3689+
///
3690+
@safe pure nothrow unittest
3691+
{
3692+
// Maximum is 4 and first occurs in position 2
3693+
int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2];
3694+
assert(a.maxIndex == 2);
3695+
3696+
// Empty range
3697+
int[] b;
3698+
assert(b.maxIndex == -1);
3699+
3700+
// Works with more custom types
3701+
struct Dog { int age; }
3702+
Dog[] dogs = [Dog(10), Dog(15), Dog(5)];
3703+
assert(dogs.maxIndex!"a.age < b.age" == 1);
3704+
}
3705+
3706+
@safe pure unittest
3707+
{
3708+
// should work with const
3709+
const(int)[] immArr = [5, 1, 3];
3710+
assert(immArr.maxIndex == 0);
3711+
3712+
// Works for const ranges too
3713+
const int[] c = [2, 5, 4, 1, 2, 3];
3714+
assert(c.maxIndex == 1);
3715+
3716+
3717+
// should work with immutable
3718+
immutable(int)[] immArr2 = [2, 1, 3];
3719+
assert(immArr2.maxIndex == 2);
3720+
3721+
// with strings
3722+
assert(["b", "a", "c"].maxIndex == 2);
3723+
3724+
// infinite range
3725+
import std.range : cycle;
3726+
static assert(!__traits(compiles, cycle([1]).maxIndex));
3727+
3728+
// with all dummy ranges
3729+
import std.internal.test.dummyrange : AllDummyRanges;
3730+
foreach (DummyType; AllDummyRanges)
3731+
{
3732+
static if (isForwardRange!DummyType && !isInfinite!DummyType)
3733+
{
3734+
DummyType d;
3735+
3736+
d.arr = [5, 3, 7, 2, 1, 4];
3737+
assert(d.maxIndex == 2);
3738+
3739+
d.arr = [];
3740+
assert(d.maxIndex == -1);
3741+
}
3742+
}
3743+
}
3744+
3745+
@nogc @safe nothrow pure unittest
3746+
{
3747+
static immutable arr = [7, 3, 8, 2, 1, 4];
3748+
assert(arr.maxIndex == 2);
3749+
3750+
static immutable arr2d = [[1, 3], [3, 9], [4, 2]];
3751+
assert(arr2d.maxIndex!"a[1] < b[1]" == 1);
3752+
}
3753+
35473754
/**
35483755
Skip over the initial portion of the first given range that matches the second
35493756
range, or do nothing if there is no match.

0 commit comments

Comments
 (0)