-
-
Notifications
You must be signed in to change notification settings - Fork 741
min/max with keyfunction + for a single range #4019
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
Conversation
auto maxValue = keyGetterFun(r.front); | ||
auto maxEl = r.front; | ||
r.popFront(); | ||
foreach(el; r){ |
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.
spaces between keywords (foreach,if,while) and the opening parenthesis
foreach (
No need to specify a
which is currently not possible with your solution. |
f9537bf
to
941aede
Compare
@burner: updated with changing regarding all your remarks! Thanks a lot! @JackStouffer isn't a |
Added, but doesn't this conflict with the existing documentation for |
have a look at the output of CyberShadows autotester it builds the docs |
@burner I just did - it produced two times |
95% of the time, yes it is. But there's no reason we can't accommodate the other 5%
Then the user either made a mistake which will be caught shortly, or for some strange reason doesn't want to use Another added benefit of going with a predicate is that you can make this DRY by having both functions use another private function as the implementation, as most of these two functions are the same code. |
@JackStouffer I added With that
|
// keygetter | ||
assert([1,5,2].enumerate.max!"a[1]".index == 1); | ||
assert([1,5,2].enumerate.max!"a[1]".value == 5); | ||
assert([1,5,2].enumerate.max!"a[1]".array == [1,5]); |
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.
IIUC, "a[1]" is the same as "a.value". Using the latter would be clearer and more consistent. I didn't understand this code until I looked up the docs for enumerate. Ditto below various times.
2nd, the .array
line is a bit confusing. Clearer might be to say the result of max == tuple(1, 5)
.
Also, the source array starts with 1,5 - perhaps change the 1 to something unique, just to make the example clearer.
@ntrel addressed and updated. thanks :) |
I'd rather see a new name for a function that finds the minimum / maximum in a range.
In the example above, it is clear what happens. But with a heavily templated code, I'd rather see a compiler error when it has to find minimum of one argument after tuple expansion than have it silently handled this way. Other than that, I like your "less participation to useless debates + more real contribution = win" attitude :) . |
@GassaFM you have a very fair point there, but I only partially agree. My personal opinion is that the violation of the principle of least astonishment weighs stronger than this silent handling of tuple expansion, because the majority (newcomers) will have to google/search for it whereas this tuple example seems seldom and still understandable. What is your final opinion about this?, e.g. @JackStouffer, @andralex and @default0 NamingThat being said I would propose to add If renaming the max/min methods is the consensus, other ideas for names would be
What names do you like? |
|
||
Returns: The lowest value according to $(D pred) of the passed-in values. | ||
*/ | ||
auto selectOne(alias pred, alias keyGetter = (a) => a, Range)(Range r) |
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.
This name is not suggestive of "extremum".
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.
We don't use the key getter style of programming most anywhere else in Phobos (though it does have appeal). For projections we use map
. Here, custom less
predicate would be used.
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.
Changed to extremum and removed the key getter.
The idea of the keygetter or map
is not only syntax sugar, the idea is also to save mapFun
calls as if I write extremum!"a.score() > b.score()
" score
would be executed twice as often.
I do admit that map
is usually an inexpensive function or just an access, but here we save in performance and have better usability.
input.extremum!((a, b) => computeDistanceTo(a) < computeDistanceTo(b))"
vs.
input.minElement!computeDistanceTo
Overall I think this is rather foreign compared to prevalent Phobos style. I think |
6ebe16c
to
972e2cc
Compare
@andralex I really like your choice Moreover I also moved it from Btw and other idea to be friendlier to newbies - how about adding a default method for |
Nice - done and added a couple of tests with |
LGTM :-) |
The issue I see with |
@JackStouffer suggested it might be useful to some people. We can also make it private for the time being? |
I'm talking about the name. |
Yes that is precisely the definition of |
In SQL one calls it |
|
||
Returns: The extreme value according to $(D less) of the passed-in values. | ||
*/ | ||
auto extremum(alias less, Range)(Range r) |
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.
The predicate probably should be named more generically, e.g. selector
.
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.
good idea - done :)
Looks like we can close this issue when this is merged: |
@@ -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 | |||
must be passed, and they must be comparable with `<`. | |||
Returns: The minimum of the passed-in values. | |||
See_Also: | |||
$(XREF algorithm,searching,minElement) |
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.
s/XREF/XREF_PACK/, or use the variadic REF
: $(REF minElement, algorithm, searching)
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.
Ah I already did this for maxElement
, but just forgot it. Thanks & updated!
Btw is there any cheatsheet
or nice overview of all these ddox magics`?
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.
I've tried to migrate all our cross-referencing over to the new variadic MREF
and REF
macros, but the PRs are stagnant. Without these new macros our situation is a mess. I'd be up for documenting our Phobos macros once we pull through with that.
@@ -33,6 +33,9 @@ $(T2 commonPrefix, | |||
$(D commonPrefix("parakeet", "parachute")) returns $(D "para").) | |||
$(T2 endsWith, | |||
$(D endsWith("rocks", "ks")) returns $(D true).) | |||
$(T2 extremum, | |||
Selects the extreme element of a range. | |||
$(D [[0, 4], [1, 2]].extremum!"a[1] > b[1]") returns $(D [0, 4]).) |
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.
Is this different from [[0, 4], [1, 2]].maxPos!((a, b) => a[1] < b[1]).front
?
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.
I guess this was already pointed out.
edit:
Or maybe not. I don't see any mention of our existing minPos
and recently also maxPos
.
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.
ping @andralex - do you still prefer |
Ping pong @andralex - or maybe other reviewers have an opinion on this too @JakobOvrum @klickverbot @jmdavis @quickfur ? |
$(D minElement([3, 4, 1, 2])) returns $(D 1).) | ||
$(T2 maxElement, | ||
Selects the maximal element of a range. | ||
$(D minElement([3, 4, 1, 2])) returns $(D 4).) |
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.
typo: should be maxElement.
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.
good spot - thanks :)
I think @JakobOvrum is right, Also, wouldn't |
I am not a friend of
AFAIK it can't be optimized away - my initial implementation was an extremum function that did this caching of the mapping function, which was criticized due to its inflexibility which would fall away if we agree that we only provide |
Could someone add the @andralex label? Thanks ;-) |
I forgot to add |
Ping pong ping pong |
It seems Andrei has already approved the names: #4019 (comment) So AIUI, one of the other reviewers with merge rights could merge this. Personally I would make extremum private though. |
Resubmitted a variant #4221 that only calls the mapping function once as we now can modify the internal |
Hey all,
this PR allows the
min
/max
function to be used with a single range and with a custom key getter.This topic has been mentioned quite often (e.g. Issue 4705), however imho the solution is trivial.
Why is this cool? Because with this we can do a couple of useful queries - most notably
argMin/argMax
, e.g.(see the tests for more examples)
A couple of comments
min/max
as