-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Iterator::take_while
stops before the first element where the condition evaluates to false
. That is the correct behavior given its name, but it makes it difficult to write an iterator that should include the boundary element. In particular consider something like iterating over a histogram, trimming off the tails:
for v in histogram
.iter_linear(1_000)
.skip_while(|v| v.quantile() < 0.01)
.take_while(|v| v.quantile() < 0.99)
This may seem right, but consider an iterator where the elements are such that v.quantile()
yields this sequence: [0, 0.02, 0.99]
. Here, the bin that spans <0.02-0.99]
will be dropped, even though it includes the majority of the samples. What we really want to do is take from the iterator until we have received an element whose v.quantile() > 0.99
. To write that with take_while
, we need something like:
let mut got_true = true;
for v in h
.iter_linear(1_000)
.skip_while(|v| v.quantile() < 0.01)
.take_while(move |v| {
if got_true {
// we've already yielded when condition was true
return false;
}
if v.quantile() > 0.99 {
// this must be the first time condition returns true
// we should yield i, and then no more
got_true = true;
}
// we should keep yielding
true
})
Which isn't exactly obvious.
So, I propose we add Iterator::take_until_inclusive
, which yields elements up to and including the first element for which its argument returns true. The name isn't great, but I'm also struggling to come up with a better one. In some sense, I want the function to communicate that it yields every item it evaluates. Some other possible names if we're willing to invert the boolean: break_once
, break_after
, end_at
, end_once
, end_after
.
Thoughts?