Last active
January 6, 2020 17:43
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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