-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Add Iterator::intersperse #75784
Add Iterator::intersperse #75784
Changes from all commits
13d5e6d
8a05fbc
f470155
97039d8
572b795
00ad5c8
d3122a8
3a069a0
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 | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,76 @@ | ||||||||||||||||||||||||||||||||||||
use super::Peekable; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
/// An iterator adapter that places a separator between all elements. | ||||||||||||||||||||||||||||||||||||
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] | ||||||||||||||||||||||||||||||||||||
#[derive(Debug, Clone)] | ||||||||||||||||||||||||||||||||||||
pub struct Intersperse<I: Iterator> | ||||||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||||||
I::Item: Clone, | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
separator: I::Item, | ||||||||||||||||||||||||||||||||||||
iter: Peekable<I>, | ||||||||||||||||||||||||||||||||||||
Comment on lines
+10
to
+11
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 believe
Suggested change
|
||||||||||||||||||||||||||||||||||||
needs_sep: bool, | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
impl<I: Iterator> Intersperse<I> | ||||||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||||||
I::Item: Clone, | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self { | ||||||||||||||||||||||||||||||||||||
Self { iter: iter.peekable(), separator, needs_sep: false } | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] | ||||||||||||||||||||||||||||||||||||
impl<I> Iterator for Intersperse<I> | ||||||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||||||
I: Iterator, | ||||||||||||||||||||||||||||||||||||
I::Item: Clone, | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
type Item = I::Item; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
#[inline] | ||||||||||||||||||||||||||||||||||||
fn next(&mut self) -> Option<I::Item> { | ||||||||||||||||||||||||||||||||||||
if self.needs_sep && self.iter.peek().is_some() { | ||||||||||||||||||||||||||||||||||||
self.needs_sep = false; | ||||||||||||||||||||||||||||||||||||
Some(self.separator.clone()) | ||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||
jonas-schievink marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||
self.needs_sep = true; | ||||||||||||||||||||||||||||||||||||
self.iter.next() | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
jonas-schievink marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
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. Should we have a version of 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. Oh, hey, I wrote this! Specializing 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. But I see that other specializations do include 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. There are several forwarding hierarchies in 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. @the8472 So it means we should implement all the important ones? 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. If the custom 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. Added an implementation of 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. You have removed |
||||||||||||||||||||||||||||||||||||
fn fold<B, F>(mut self, init: B, mut f: F) -> B | ||||||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||||||
Self: Sized, | ||||||||||||||||||||||||||||||||||||
F: FnMut(B, Self::Item) -> B, | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
let mut accum = init; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
// Use `peek()` first to avoid calling `next()` on an empty iterator. | ||||||||||||||||||||||||||||||||||||
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. That's only useful for iterators that are not fused, right? Or what do we gain by not calling Also, I think this would break for the following case:
That would result in |
||||||||||||||||||||||||||||||||||||
if !self.needs_sep || self.iter.peek().is_some() { | ||||||||||||||||||||||||||||||||||||
if let Some(x) = self.iter.next() { | ||||||||||||||||||||||||||||||||||||
accum = f(accum, x); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
Comment on lines
+50
to
+55
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. Wouldn't this result in I thought fold should always start with an 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.
Why would that be? As far as I see it, that's intended behavior. The iterator is just a sequence of items and if you already got the first one, methods like |
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
let element = &self.separator; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
self.iter.fold(accum, |mut accum, x| { | ||||||||||||||||||||||||||||||||||||
accum = f(accum, element.clone()); | ||||||||||||||||||||||||||||||||||||
accum = f(accum, x); | ||||||||||||||||||||||||||||||||||||
accum | ||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||
LukasKalbertodt marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
fn size_hint(&self) -> (usize, Option<usize>) { | ||||||||||||||||||||||||||||||||||||
let (lo, hi) = self.iter.size_hint(); | ||||||||||||||||||||||||||||||||||||
let next_is_elem = !self.needs_sep; | ||||||||||||||||||||||||||||||||||||
let lo = lo.saturating_sub(next_is_elem as usize).saturating_add(lo); | ||||||||||||||||||||||||||||||||||||
let hi = match hi { | ||||||||||||||||||||||||||||||||||||
Some(hi) => hi.saturating_sub(next_is_elem as usize).checked_add(hi), | ||||||||||||||||||||||||||||||||||||
None => None, | ||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||
Comment on lines
+68
to
+73
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. Would this be better?
Suggested change
Comment on lines
+70
to
+73
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. No hard opinion, but this should work as well:
Suggested change
|
||||||||||||||||||||||||||||||||||||
(lo, hi) | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,7 +8,7 @@ use crate::ops::{Add, ControlFlow, Try}; | |||||||||||||||||
use super::super::TrustedRandomAccess; | ||||||||||||||||||
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; | ||||||||||||||||||
use super::super::{FlatMap, Flatten}; | ||||||||||||||||||
use super::super::{FromIterator, Product, Sum, Zip}; | ||||||||||||||||||
use super::super::{FromIterator, Intersperse, Product, Sum, Zip}; | ||||||||||||||||||
use super::super::{ | ||||||||||||||||||
Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, | ||||||||||||||||||
}; | ||||||||||||||||||
|
@@ -536,6 +536,28 @@ pub trait Iterator { | |||||||||||||||||
Zip::new(self, other.into_iter()) | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// Places a copy of `separator` between all elements. | ||||||||||||||||||
jonas-schievink marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
/// | ||||||||||||||||||
/// # Examples | ||||||||||||||||||
/// | ||||||||||||||||||
/// Basic usage: | ||||||||||||||||||
/// | ||||||||||||||||||
/// ``` | ||||||||||||||||||
/// #![feature(iter_intersperse)] | ||||||||||||||||||
/// | ||||||||||||||||||
/// let hello = ["Hello", "World"].iter().copied().intersperse(" ").collect::<String>(); | ||||||||||||||||||
/// assert_eq!(hello, "Hello World"); | ||||||||||||||||||
Comment on lines
+548
to
+549
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. Add different example and some humor (not tested)
Suggested change
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'd rather not 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. Haha, how about the second example for 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. Another example where |
||||||||||||||||||
/// ``` | ||||||||||||||||||
#[inline] | ||||||||||||||||||
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "none")] | ||||||||||||||||||
fn intersperse(self, separator: Self::Item) -> Intersperse<Self> | ||||||||||||||||||
where | ||||||||||||||||||
Self: Sized, | ||||||||||||||||||
Self::Item: Clone, | ||||||||||||||||||
{ | ||||||||||||||||||
Intersperse::new(self, separator) | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// Takes a closure and creates an iterator which calls that closure on each | ||||||||||||||||||
/// element. | ||||||||||||||||||
/// | ||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -3222,3 +3222,85 @@ fn test_flatten_non_fused_inner() { | |||||
assert_eq!(iter.next(), Some(1)); | ||||||
assert_eq!(iter.next(), None); | ||||||
} | ||||||
|
||||||
#[test] | ||||||
fn test_intersperse() { | ||||||
let xs = ["a", "", "b", "c"]; | ||||||
let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect(); | ||||||
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.
Suggested change
|
||||||
let text: String = v.concat(); | ||||||
assert_eq!(text, "a, , b, c".to_string()); | ||||||
|
||||||
let ys = [0, 1, 2, 3]; | ||||||
let mut it = ys[..0].iter().map(|x| *x).intersperse(1); | ||||||
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.
Suggested change
Not sure if this works but probably it does. |
||||||
assert!(it.next() == None); | ||||||
} | ||||||
|
||||||
#[test] | ||||||
fn test_intersperse_size_hint() { | ||||||
let xs = ["a", "", "b", "c"]; | ||||||
let mut iter = xs.iter().map(|x| x.clone()).intersperse(", "); | ||||||
assert_eq!(iter.size_hint(), (7, Some(7))); | ||||||
|
||||||
assert_eq!(iter.next(), Some("a")); | ||||||
assert_eq!(iter.size_hint(), (6, Some(6))); | ||||||
assert_eq!(iter.next(), Some(", ")); | ||||||
assert_eq!(iter.size_hint(), (5, Some(5))); | ||||||
|
||||||
assert_eq!([].iter().intersperse(&()).size_hint(), (0, Some(0))); | ||||||
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. Should we add a test for one without upper bound? I think this is sufficient but just wondering if we need it since someone could change |
||||||
} | ||||||
|
||||||
#[test] | ||||||
fn test_fold_specialization_intersperse() { | ||||||
let mut iter = (1..2).intersperse(0); | ||||||
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); | ||||||
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. Should we add a test for |
||||||
|
||||||
let mut iter = (1..3).intersperse(0); | ||||||
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); | ||||||
|
||||||
let mut iter = (1..4).intersperse(0); | ||||||
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); | ||||||
Comment on lines
+3257
to
+3261
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 don't understand what is the difference between these and the above test? Do we need these? |
||||||
} | ||||||
|
||||||
#[test] | ||||||
fn test_try_fold_specialization_intersperse_ok() { | ||||||
let mut iter = (1..2).intersperse(0); | ||||||
iter.clone().try_for_each(|x| { | ||||||
assert_eq!(Some(x), iter.next()); | ||||||
Some(()) | ||||||
}); | ||||||
|
||||||
let mut iter = (1..3).intersperse(0); | ||||||
iter.clone().try_for_each(|x| { | ||||||
assert_eq!(Some(x), iter.next()); | ||||||
Some(()) | ||||||
}); | ||||||
|
||||||
let mut iter = (1..4).intersperse(0); | ||||||
iter.clone().try_for_each(|x| { | ||||||
assert_eq!(Some(x), iter.next()); | ||||||
Some(()) | ||||||
}); | ||||||
} | ||||||
|
||||||
#[test] | ||||||
fn test_try_fold_specialization_intersperse_err() { | ||||||
let orig_iter = ["a", "b"].iter().copied().intersperse("-"); | ||||||
|
||||||
// Abort after the first item. | ||||||
let mut iter = orig_iter.clone(); | ||||||
iter.try_for_each(|_| None::<()>); | ||||||
assert_eq!(iter.next(), Some("-")); | ||||||
assert_eq!(iter.next(), Some("b")); | ||||||
assert_eq!(iter.next(), None); | ||||||
|
||||||
// Abort after the second item. | ||||||
let mut iter = orig_iter.clone(); | ||||||
iter.try_for_each(|item| if item == "-" { None } else { Some(()) }); | ||||||
assert_eq!(iter.next(), Some("b")); | ||||||
assert_eq!(iter.next(), None); | ||||||
|
||||||
// Abort after the third item. | ||||||
let mut iter = orig_iter.clone(); | ||||||
iter.try_for_each(|item| if item == "b" { None } else { Some(()) }); | ||||||
assert_eq!(iter.next(), None); | ||||||
} |
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.
Document how was in created.