-
-
Notifications
You must be signed in to change notification settings - Fork 748
Issue 8573 - A simpler Phobos function that returns the index of the … #4921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3544,6 +3544,213 @@ unittest | |
| assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]); | ||
| } | ||
|
|
||
| /** | ||
| Computes the index of the first occurrence of `range`'s minimum element. | ||
|
|
||
| Params: | ||
| pred = The ordering predicate to use to determine the minimum element. | ||
| range = The input range to search. | ||
|
|
||
| Complexity: O(n) | ||
| Exactly `n - 1` comparisons are needed. | ||
|
|
||
| Returns: | ||
| The index of the first encounter of the minimum element in `range`. If the | ||
| `range` is empty, -1 is returned. | ||
|
|
||
| See_Also: | ||
| $(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos) | ||
| */ | ||
| sizediff_t minIndex(alias pred = "a < b", Range)(Range range) | ||
| if (isForwardRange!Range && !isInfinite!Range && | ||
| is(typeof(binaryFun!pred(range.front, range.front)))) | ||
|
||
| { | ||
| if (range.empty) return -1; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For efficiency in release mode, In that case we can use
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we're good here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This runs counter to the behavior of most range functions in Phobos. Normally an empty range is rejected outright with an assert rather than given a special value. I would change this to
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say we can return -1 here
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I struggled with this for a bit. We have
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @9il thx. @JackStouffer other precedents we may draw from? |
||
|
|
||
| sizediff_t minPos = 0; | ||
|
|
||
| static if (isRandomAccessRange!Range && hasLength!Range) | ||
| { | ||
| foreach (i; 1 .. range.length) | ||
| { | ||
| if (binaryFun!pred(range[i], range[minPos])) | ||
| { | ||
| minPos = i; | ||
| } | ||
| } | ||
| } | ||
| else | ||
| { | ||
| sizediff_t curPos = 0; | ||
| Unqual!(typeof(range.front)) min = range.front; | ||
| for (range.popFront(); !range.empty; range.popFront()) | ||
| { | ||
| ++curPos; | ||
| if (binaryFun!pred(range.front, min)) | ||
| { | ||
| min = range.front; | ||
| minPos = curPos; | ||
| } | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a special path for arrays - unfortunately it will be a lot faster. |
||
| return minPos; | ||
| } | ||
|
|
||
| /// | ||
| @safe pure nothrow unittest | ||
| { | ||
| int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; | ||
|
|
||
| // Minimum is 1 and first occurs in position 3 | ||
| assert(a.minIndex == 3); | ||
| // Get maximum index with minIndex | ||
| assert(a.minIndex!"a > b" == 2); | ||
|
|
||
| // Range is empty, so return value is -1 | ||
| int[] b; | ||
| assert(b.minIndex == -1); | ||
|
|
||
| // Works with more custom types | ||
| struct Dog { int age; } | ||
| Dog[] dogs = [Dog(10), Dog(5), Dog(15)]; | ||
| assert(dogs.minIndex!"a.age < b.age" == 1); | ||
| } | ||
|
|
||
| @safe pure unittest | ||
| { | ||
| // should work with const | ||
| const(int)[] immArr = [2, 1, 3]; | ||
| assert(immArr.minIndex == 1); | ||
|
|
||
| // Works for const ranges too | ||
| const int[] c = [2, 5, 4, 1, 2, 3]; | ||
| assert(c.minIndex == 3); | ||
|
|
||
| // should work with immutable | ||
| immutable(int)[] immArr2 = [2, 1, 3]; | ||
| assert(immArr2.minIndex == 1); | ||
|
|
||
| // with strings | ||
| assert(["b", "a", "c"].minIndex == 1); | ||
|
|
||
| // infinite range | ||
| import std.range : cycle; | ||
| static assert(!__traits(compiles, cycle([1]).minIndex)); | ||
|
|
||
| // with all dummy ranges | ||
| import std.internal.test.dummyrange : AllDummyRanges; | ||
| foreach (DummyType; AllDummyRanges) | ||
| { | ||
| static if (isForwardRange!DummyType && !isInfinite!DummyType) | ||
| { | ||
| DummyType d; | ||
| d.arr = [5, 3, 7, 2, 1, 4]; | ||
| assert(d.minIndex == 4); | ||
|
|
||
| d.arr = []; | ||
| assert(d.minIndex == -1); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @nogc @safe nothrow pure unittest | ||
| { | ||
| static immutable arr = [7, 3, 8, 2, 1, 4]; | ||
| assert(arr.minIndex == 4); | ||
|
|
||
| static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; | ||
| assert(arr2d.minIndex!"a[1] < b[1]" == 2); | ||
| } | ||
|
|
||
| /** | ||
| Computes the index of the first occurrence of `range`'s maximum element. | ||
|
|
||
| Complexity: O(n) | ||
| Exactly `n - 1` comparisons are needed. | ||
|
|
||
| Params: | ||
| pred = The ordering predicate to use to determine the maximum element. | ||
| range = The input range to search. | ||
|
|
||
| Returns: | ||
| The index of the first encounter of the maximum in `range`. If the | ||
| `range` is empty, -1 is returned. | ||
|
|
||
| See_Also: | ||
| $(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos) | ||
| */ | ||
| sizediff_t maxIndex(alias pred = "a < b", Range)(Range range) | ||
| if (isInputRange!Range && !isInfinite!Range && | ||
| is(typeof(binaryFun!pred(range.front, range.front)))) | ||
| { | ||
| return range.minIndex!((a, b) => binaryFun!pred(b, a)); | ||
| } | ||
|
|
||
| /// | ||
| @safe pure nothrow unittest | ||
| { | ||
| // Maximum is 4 and first occurs in position 2 | ||
| int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; | ||
| assert(a.maxIndex == 2); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add the emptyness example here as well |
||
| // Empty range | ||
| int[] b; | ||
| assert(b.maxIndex == -1); | ||
|
|
||
| // Works with more custom types | ||
| struct Dog { int age; } | ||
| Dog[] dogs = [Dog(10), Dog(15), Dog(5)]; | ||
| assert(dogs.maxIndex!"a.age < b.age" == 1); | ||
| } | ||
|
|
||
| @safe pure unittest | ||
| { | ||
| // should work with const | ||
| const(int)[] immArr = [5, 1, 3]; | ||
| assert(immArr.maxIndex == 0); | ||
|
|
||
| // Works for const ranges too | ||
| const int[] c = [2, 5, 4, 1, 2, 3]; | ||
| assert(c.maxIndex == 1); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tests should also test more than the default string lambda.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it ok now?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, thank you |
||
|
|
||
|
|
||
| // should work with immutable | ||
| immutable(int)[] immArr2 = [2, 1, 3]; | ||
| assert(immArr2.maxIndex == 2); | ||
|
|
||
| // with strings | ||
| assert(["b", "a", "c"].maxIndex == 2); | ||
|
|
||
| // infinite range | ||
| import std.range : cycle; | ||
| static assert(!__traits(compiles, cycle([1]).maxIndex)); | ||
|
|
||
| // with all dummy ranges | ||
| import std.internal.test.dummyrange : AllDummyRanges; | ||
| foreach (DummyType; AllDummyRanges) | ||
| { | ||
| static if (isForwardRange!DummyType && !isInfinite!DummyType) | ||
| { | ||
| DummyType d; | ||
|
|
||
| d.arr = [5, 3, 7, 2, 1, 4]; | ||
| assert(d.maxIndex == 2); | ||
|
|
||
| d.arr = []; | ||
| assert(d.maxIndex == -1); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @nogc @safe nothrow pure unittest | ||
| { | ||
| static immutable arr = [7, 3, 8, 2, 1, 4]; | ||
| assert(arr.maxIndex == 2); | ||
|
|
||
| static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; | ||
| assert(arr2d.maxIndex!"a[1] < b[1]" == 1); | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add additional tests for
|
||
| /** | ||
| Skip over the initial portion of the first given range that matches the second | ||
| range, or do nothing if there is no match. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about