Skip to content

Commit 7f7f828

Browse files
committed
Add itertools::generate
It's an easier to use alternative to unfoled, modeled after Kotlin's generateSequence.
1 parent bfe6167 commit 7f7f828

File tree

3 files changed

+93
-5
lines changed

3 files changed

+93
-5
lines changed

src/lib.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub mod structs {
9191
#[cfg(feature = "use_std")]
9292
pub use rciter_impl::RcIter;
9393
pub use repeatn::RepeatN;
94-
pub use sources::{RepeatCall, Unfold, Iterate};
94+
pub use sources::{RepeatCall, Generate, Unfold, Iterate};
9595
#[cfg(feature = "use_std")]
9696
pub use tee::Tee;
9797
pub use tuple_impl::{TupleBuffer, TupleWindows, Tuples};
@@ -114,7 +114,7 @@ pub use minmax::MinMaxResult;
114114
pub use peeking_take_while::PeekingNext;
115115
pub use process_results_impl::process_results;
116116
pub use repeatn::repeat_n;
117-
pub use sources::{repeat_call, unfold, iterate};
117+
pub use sources::{repeat_call, generate, unfold, iterate};
118118
pub use with_position::Position;
119119
pub use ziptuple::multizip;
120120
mod adaptors;
@@ -1888,13 +1888,13 @@ pub trait Itertools : Iterator {
18881888

18891889
/// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values
18901890
/// are taken from `(Key, Value)` tuple pairs yielded by the input iterator.
1891-
///
1891+
///
18921892
/// ```
18931893
/// use itertools::Itertools;
1894-
///
1894+
///
18951895
/// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)];
18961896
/// let lookup = data.into_iter().into_group_map();
1897-
///
1897+
///
18981898
/// assert_eq!(lookup[&0], vec![10, 20]);
18991899
/// assert_eq!(lookup.get(&1), None);
19001900
/// assert_eq!(lookup[&2], vec![12, 42]);

src/sources.rs

+77
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,83 @@ impl<A, St, F> Iterator for Unfold<St, F>
135135
}
136136
}
137137

138+
/// Returns an iterator that starts with the `seed` and repeatedly
139+
/// calls the `step` function to produce the next value, until it
140+
/// returns `None`. The resulting sequence always has at least one
141+
/// element -- the `seed`. To produce a possibly empty sequence,
142+
/// use `Generate::new`.
143+
///
144+
/// ```
145+
/// // an iterator that yields Collatz sequence.
146+
/// // https://en.wikipedia.org/wiki/Collatz_conjecture
147+
///
148+
/// use itertools::generate;
149+
///
150+
/// let collatz = generate(12, |&n| {
151+
/// if n == 1 {
152+
/// None
153+
/// } else if n % 2 == 0 {
154+
/// Some(n / 2)
155+
/// } else {
156+
/// Some(3 * n + 1)
157+
/// }
158+
/// });
159+
///
160+
/// itertools::assert_equal(collatz,
161+
/// vec![12, 6, 3, 10, 5, 16, 8, 4, 2, 1]);
162+
/// ```
163+
pub fn generate<T, F>(seed: T, step: F) -> Generate<T, F>
164+
where F: FnMut(&T) -> Option<T>
165+
{
166+
Generate::new(Some(seed), step)
167+
}
168+
169+
impl<T, F> fmt::Debug for Generate<T, F>
170+
where T: fmt::Debug,
171+
{
172+
debug_fmt_fields!(Genrate, next);
173+
}
174+
175+
/// See [`generate`](../fn.generate.html) for more information.
176+
#[derive(Clone)]
177+
#[must_use = "iterators are lazy and do nothing unless consumed"]
178+
pub struct Generate<T, F> {
179+
next: Option<T>,
180+
step: F,
181+
}
182+
183+
impl<T, F> Generate<T, F>
184+
where F: FnMut(&T) -> Option<T>
185+
{
186+
/// Like [`generate`](../fn.generate.html), but allows for
187+
/// empty sequences.
188+
pub fn new(seed: Option<T>, step: F) -> Generate<T, F> {
189+
Generate {
190+
next: seed,
191+
step: step,
192+
}
193+
}
194+
}
195+
196+
impl<T, F> Iterator for Generate<T, F>
197+
where F: FnMut(&T) -> Option<T>
198+
{
199+
type Item = T;
200+
201+
#[inline]
202+
fn next(&mut self) -> Option<T> {
203+
self.next.take().map(|next| {
204+
self.next = (self.step)(&next);
205+
next
206+
})
207+
}
208+
209+
#[inline]
210+
fn size_hint(&self) -> (usize, Option<usize>) {
211+
(self.next.is_some() as usize, None)
212+
}
213+
}
214+
138215
/// An iterator that infinitely applies function to value and yields results.
139216
///
140217
/// This `struct` is created by the [`iterate()`] function. See its documentation for more.

tests/test_core.rs

+11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use it::Itertools;
1212
use it::interleave;
1313
use it::multizip;
1414
use it::free::put_back;
15+
use it::{generate, Generate};
1516

1617
#[test]
1718
fn product2() {
@@ -238,3 +239,13 @@ fn tree_fold1() {
238239
assert_eq!((0..i).tree_fold1(|x, y| x + y), (0..i).fold1(|x, y| x + y));
239240
}
240241
}
242+
243+
#[test]
244+
fn test_generate() {
245+
let count_down = generate(5u32, |&n| n.checked_sub(1));
246+
it::assert_equal(count_down, [5, 4, 3, 2, 1, 0].iter().cloned());
247+
let non_empty = generate((), |&()| None);
248+
assert_eq!(non_empty.count(), 1);
249+
let empty = Generate::new(None, |&()| None);
250+
assert_eq!(empty.count(), 0);
251+
}

0 commit comments

Comments
 (0)