Skip to content
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

improve documentation of tree_reduce #955

Merged
merged 4 commits into from
Jun 17, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2528,34 +2528,64 @@ pub trait Itertools: Iterator {
///
/// If `f` is associative you should also decide carefully:
///
/// - if `f` is a trivial operation like `u32::wrapping_add`, prefer the normal
/// [`Iterator::reduce`] instead since it will most likely result in the generation of simpler
/// code because the compiler is able to optimize it
/// - otherwise if `f` is non-trivial like `format!`, you should use `tree_reduce` since it
/// reduces the number of operations from `O(n)` to `O(ln(n))`
Philippe-Cholet marked this conversation as resolved.
Show resolved Hide resolved
/// For an iterator producing `n` elements, both [`Iterator::reduce`] and `tree_reduce` will
/// call `f` `n - 1` times. However, `tree_reduce` will call `f` on earlier intermediate
/// results, which is beneficial for `f` that allocate and produce longer results for longer
Philippe-Cholet marked this conversation as resolved.
Show resolved Hide resolved
/// arguments. For example if `f` combines arguments using `format!`, then `tree_reduce` will
/// operate on average on shorter arguments resulting in less bytes being allocated overall.
///
/// Here "non-trivial" means:
/// Moreover, the output of `tree_reduce` is preferable to that of [`Iterator::reduce`] in
/// certain cases. For example, building a binary search tree using `tree_reduce` will result in
/// a balanced tree with height `O(ln(n))`, while [`Iterator::reduce`] will output a tree with
/// height `O(n)`, essentially a linked list.
///
/// - any allocating operation
/// - any function that is a composition of many operations
/// If `f` does not benefit from such a reordering, like `u32::wrapping_add`, prefer the
/// normal [`Iterator::reduce`] instead since it will most likely result in the generation of
/// simpler code because the compiler is able to optimize it.
///
/// ```
/// use itertools::Itertools;
///
/// let f = |a: String, b: String| {
/// format!("f({a}, {b})")
/// };
///
/// // The same tree as above
/// let num_strings = (1..8).map(|x| x.to_string());
/// assert_eq!(num_strings.tree_reduce(|x, y| format!("f({}, {})", x, y)),
/// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
/// assert_eq!((1..8).map(|x| x.to_string()).tree_reduce(f),
/// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
///
/// // Like fold1, an empty iterator produces None
/// assert_eq!((0..0).tree_reduce(|x, y| x * y), None);
///
/// // tree_reduce matches fold1 for associative operations...
/// assert_eq!((0..10).tree_reduce(|x, y| x + y),
/// (0..10).fold1(|x, y| x + y));
///
/// // ...but not for non-associative ones
/// assert_ne!((0..10).tree_reduce(|x, y| x - y),
/// (0..10).fold1(|x, y| x - y));
///
/// let mut total_len_reduce = 0;
/// let reduce_res = (1..100).map(|x| x.to_string())
/// .reduce(|a, b| {
/// let r = f(a, b);
/// total_len_reduce += r.len();
/// r
/// })
/// .unwrap();
///
/// let mut total_len_tree_reduce = 0;
/// let tree_reduce_res = (1..100).map(|x| x.to_string())
/// .tree_reduce(|a, b| {
/// let r = f(a, b);
/// total_len_tree_reduce += r.len();
/// r
/// })
/// .unwrap();
///
/// assert_eq!(total_len_reduce, 33299);
/// assert_eq!(total_len_tree_reduce, 4228);
/// assert_eq!(reduce_res.len(), tree_reduce_res.len());
/// ```
fn tree_reduce<F>(mut self, mut f: F) -> Option<Self::Item>
where
Expand Down