Skip to content

Tracking issue for {min,max} on iterators #27724

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

Closed
alexcrichton opened this issue Aug 12, 2015 · 18 comments
Closed

Tracking issue for {min,max} on iterators #27724

alexcrichton opened this issue Aug 12, 2015 · 18 comments
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@alexcrichton
Copy link
Member

This is a tracking issue for the unstable iter_cmp feature in the standard library. This is the ability to get the min or max elements out of an iterator via a custom closure for comparisons.

Some open questions are:

  • Is _by the right suffix here? The stable <[T]>::sort_by function has a different signature than these two functions, so these may want to change suffix to something like min_with.
  • Is it useful to compare with a closure from &A to B? Lifetimes could perhaps get in the way where interior references to A could not be returned (e.g. comparing by just the String name of a person, not their height as well).
@alexcrichton alexcrichton added A-iterators T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. B-unstable Blocker: Implemented in the nightly compiler and unstable. labels Aug 12, 2015
@nagisa
Copy link
Member

nagisa commented Aug 12, 2015

Is _by the right suffix here?

Haskell uses by as well, if that has any weight.

@nagisa
Copy link
Member

nagisa commented Nov 1, 2015

Used min_by/max_by today. Very useful addition.

@alexcrichton
Copy link
Member Author

Nominating for discussion in 1.6

@aturon
Copy link
Member

aturon commented Nov 4, 2015

I'm in favor of stabilizing something here, but don't want to keep the _by suffixes with these signatures (given that sort_by is stable).

@malbarbo
Copy link
Contributor

malbarbo commented Nov 5, 2015

Agree with @aturon.

@alexcrichton
Copy link
Member Author

🔔 This issue is now entering its cycle-long final comment period for stabilization 🔔

The libs team discussed this and the conclusion was to stabilize this via one of two routes:

  1. Change these functions to match sort_by where the closure takes two arguments and ranks them.
  2. Rename these functions to have a different suffix and retain the existing semantics.

We're a little up in the air about which of these should be done, but I would personally lean towards option (1) to have the closure take two arguments.

@alexcrichton alexcrichton added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed I-nominated labels Nov 5, 2015
@cuviper
Copy link
Member

cuviper commented Nov 5, 2015

I like the existing semantics better, because it reduces how many times the comparison keys must be computed, in case that's an expensive thing to produce. But I can also appreciate the lifetime issues of trying to key interior references.

Can we just have both flavors? (min_by for comparison functions, min_with for key functions)

@aturon
Copy link
Member

aturon commented Nov 5, 2015

@cuviper We discussed having both flavors at the libs meeting, and there is general support for the idea, but I worry that there will be a paper cut around which of _by and _with to use. If we can come up with a really evocative name, say min_ranked or something, then I'd be in favor of supporting both.

@alexcrichton
Copy link
Member Author

One note brought up by @huonw is that we could also have something like:

struct Rank2Compare<F>(F);

impl<A, B, F> FnMut(A, A) -> Ordering for Rank2Compare<F> 
    where F: FnMut(A) -> B, B: Ord
{ /* ... */ }

iter.min_by(|a, b| a.field1.cmp(&b.field1));
iter.min_by(Rank2Compare(|a| &a.field1));

Which is to say that the "compare a and b" function is the most general, so we could build up "min by via a ranking function" on top with a special comparator. Note that the name "Rank2Compare" is pretty awful, and there may not be a fantastic name for this :)

@cuviper
Copy link
Member

cuviper commented Nov 5, 2015

How about min_by_key/min_by_rank, then min_by is "by whatever, you order it." (matching sort_by)

"Rank" is weird to me here, but maybe I'm just used to Python's min(..., key=...).

@aturon
Copy link
Member

aturon commented Nov 5, 2015

@cuviper _by_key and _by sound great to me!

@huonw
Copy link
Member

huonw commented Nov 5, 2015

Haskell calls Rank2Compare "comparing", which flows kinda nicely iter.min_by(comparing(|x| &x.field1)). (This approach works naturally with, say, sort_by, without requiring an extra function there too.)

One downside to the .min_by(comparing(...)) approach is one has to recompute the key/rank of the minimum each time, whereas a specialised min_by_key can be more efficient by storing it.

@fenhl
Copy link
Contributor

fenhl commented Nov 11, 2015

I agree that max_by and min_by should match sort_by for consistency, but coming from Python, comparing by a key seems like a more intuitive approach. What would it take to also get {max,min,sort}_by_key functions added?

@BurntSushi
Copy link
Member

I agree that we should have {min,max}_by methods to match sort_by. I know I personally get a paper cut with the as_ref/by_ref naming, so it would be nice to avoid that here! Adding {min,max,sort}_by_key seems harmless/low mental burden, but the additional functions seem unfortunate.

@alexcrichton
Copy link
Member Author

This was discussed in the libs team triage today and the decision was to stabilize with the _by_key suffixes here.

@aturon
Copy link
Member

aturon commented Dec 3, 2015

After our team discussion, I was feeling a bit suspicious about the signature:

fn min_by<B, F>(self, f: F) -> Option<Self::Item> where B: Ord, F: FnMut(&Self::Item) -> B

As @alexcrichton points out in the original issue, it's not obvious that you can return interior references here (e.g. projecting out a component of a struct), because the return type B does not scope over the borrow of &Self::Item (which would require HKT).

And, in fact, this is largely the case -- although in some cases an iterator over references is able to do such a projection:

fn main() {
    let v = vec![(0, vec![0, 1])];
    // this is OK -- the use of `iter` means there's a layer of reference
    // internally whose lifetime you can use for the output reference.
    v.iter().min_by(|v| &v.1); 

    // for some reason, this doesn't work:
    // v.iter().min_by(|v| v); 
    // v.iter().min_by(|v| &*v);     

    // this is not OK, no internal reference:
    // v.into_iter().min_by(|v| &v.1); 
}

This is unfortunate, but I think should not block stabilization; we just don't have a way to allow both extracting interior references and computing new owned values. Perhaps with HKT there will be a backcompat way to add this functionality later.

@malbarbo
Copy link
Contributor

malbarbo commented Dec 3, 2015

@aturon

v.iter().min_by(|v| a);
v.iter().min_by(|v| &*a);

the type of a is &&Vec, which appears to not implement Ord. Its strange because &T implements Ord if T does, so &A with A=&T should implement Ord, maybe this is a bug? Anyway, *a solve this case.

I don't know how to deal with the example v.into_iter().min_by(|v| &v.1), which is a valid use case.

@alexcrichton
Copy link
Member Author

The methods max_by_key and min_by_key are now stable, so closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

8 participants