Skip to content

Instantly share code, notes, and snippets.

@Kerollmops
Last active January 6, 2020 17:43
Show Gist options
  • Save Kerollmops/5da7c03b6601d63b4345173f895756a6 to your computer and use it in GitHub Desktop.
Save Kerollmops/5da7c03b6601d63b4345173f895756a6 to your computer and use it in GitHub Desktop.
A kit of simple adapter Iterators for tricky situations (no external library needed)
/// An adapter function that returns an iterator associated
/// to a boolean indicating if this is the first item returned.
///
/// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7ea95cd91a26de21c680ad7de8669c8d
///
/// ```
/// let words = ["Hello", "world", "!"];
/// let mut iter = is_first(&words);
///
/// assert_eq!(iter.next(), Some((true, "Hello")));
/// assert_eq!(iter.next(), Some((false, "world")));
/// assert_eq!(iter.next(), Some((false, "!")));
/// assert_eq!(iter.next(), None);
/// ```
fn is_first<I: IntoIterator>(iter: I) -> impl Iterator<Item=(bool, I::Item)> {
let mut iter = iter.into_iter();
let mut is_first = true;
core::iter::from_fn(move || {
iter.next().map(|item| (core::mem::take(&mut is_first), item))
})
}
/// An adapter function that returns an iterator associated
/// to a boolean indicating if this is the last item returned.
///
/// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8736adeff11e6e70f6415a89d3acfc80
///
/// ```
/// let words = ["Hello", "world", "!"];
/// let mut iter = is_last(&words);
///
/// assert_eq!(iter.next(), Some((false, "Hello")));
/// assert_eq!(iter.next(), Some((false, "world")));
/// assert_eq!(iter.next(), Some((true, "!")));
/// assert_eq!(iter.next(), None);
/// ```
fn is_last<I: IntoIterator>(iter: I) -> impl Iterator<Item=(bool, I::Item)> {
let mut iter = iter.into_iter().peekable();
core::iter::from_fn(move || {
iter.next().map(|item| (iter.peek().is_none(), item))
})
}
/// An adapter function that returns an iterator associated to a state.
/// The previous state is given to a function that computes and returns
/// the next state along with the item.
///
/// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=759c48f960571e6a29c01effef05d8b8
///
/// ```
/// let slice = ['l', 'o', 'r', 'e', 'm'];
///
/// let iter = slice.chunks(2);
/// let mut iter = tuple_fold(0, |count, chunk| count + chunk.len(), iter);
///
/// assert_eq!(iter.next(), Some((0, &['l', 'o'][..])));
/// assert_eq!(iter.next(), Some((2, &['r', 'e'][..])));
/// assert_eq!(iter.next(), Some((4, &['m'][..])));
/// assert_eq!(iter.next(), None);
/// ```
fn tuple_fold<S, F, I>(state: S, f: F, iter: I) -> impl Iterator<Item=(S, I::Item)>
where S: Clone,
F: Fn(S, &I::Item) -> S,
I: IntoIterator,
{
let mut iter = iter.into_iter();
let mut state = Some(state);
core::iter::from_fn(move || {
iter.next().map(|item| {
let s = state.take().unwrap();
state = Some(f(s.clone(), &item));
(s, item)
})
})
}
/// An adapter function that returns the ngram combinations
/// of the items in the given slice.
///
/// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=310d1b8f088498567528b4020991b315
///
/// ```
/// let words = &["This", "is", "2020"];
///
/// let mut iter = ngram_slice(3, words);
/// assert_eq!(iter.next(), Some(&["This"][..]));
/// assert_eq!(iter.next(), Some(&["This", "is"][..]));
/// assert_eq!(iter.next(), Some(&["This", "is", "2020"][..]));
/// assert_eq!(iter.next(), Some(&["is"][..]));
/// assert_eq!(iter.next(), Some(&["is", "2020"][..]));
/// assert_eq!(iter.next(), Some(&["2020"][..]));
/// assert_eq!(iter.next(), None);
/// ```
fn ngram_slice<T>(ngram: usize, slice: &[T]) -> impl Iterator<Item=&[T]> {
(0..slice.len()).flat_map(move |i| {
(1..=ngram).into_iter().filter_map(move |n| slice.get(i..i + n))
})
}
/// An adapter function that groups in a `Vec` runs of items
/// based on the given equality function.
///
/// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7e8fef55be2085c915631f2fffaed446
///
/// ```
// let words = &["hello", "happy", "world", "wagon", "zoo"];
//
// let mut iter = group_by(words, |a, b| a.chars().first() == b.chars().first());
// assert_eq!(iter.next(), Some(vec!["hello", "happy"]));
// assert_eq!(iter.next(), Some(vec!["world", "wagon"]));
// assert_eq!(iter.next(), Some(vec!["zoo"]));
// assert_eq!(iter.next(), None);
/// ```
fn group_by<I, F>(iter: I, f: F) -> impl Iterator<Item=Vec<I::Item>>
where I: IntoIterator,
F: Fn(&I::Item, &I::Item) -> bool,
{
let mut iter = iter.into_iter();
let mut prev = None;
core::iter::from_fn(move || {
let mut out = Vec::new();
loop {
match (prev.take().or_else(|| iter.next()), iter.next()) {
(Some(a), Some(b)) if f(&a, &b) => {
out.push(a);
prev = Some(b);
},
(Some(a), Some(b)) => {
out.push(a);
prev = Some(b);
return Some(out);
},
(Some(a), None) => {
out.push(a);
return Some(out);
},
(None, _) => return None,
}
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment