@@ -2528,34 +2528,63 @@ pub trait Itertools: Iterator {
25282528 ///
25292529 /// If `f` is associative you should also decide carefully:
25302530 ///
2531- /// - if `f` is a trivial operation like `u32::wrapping_add`, prefer the normal
2532- /// [`Iterator::reduce`] instead since it will most likely result in the generation of simpler
2533- /// code because the compiler is able to optimize it
2534- /// - otherwise if `f` is non-trivial like `format!`, you should use `tree_reduce` since it
2535- /// reduces the number of operations from `O(n)` to `O(ln(n))`
2531+ /// For an iterator producing `n` elements, both [`Iterator::reduce`] and `tree_reduce` will
2532+ /// call `f` `n - 1` times. However, `tree_reduce` will call `f` on earlier intermediate
2533+ /// results, which is beneficial for `f` that allocate and produce longer results for longer
2534+ /// arguments. For example if `f` combines arguments using `format!`, then `tree_reduce` will
2535+ /// operate on average on shorter arguments resulting in less bytes being allocated overall.
25362536 ///
2537- /// Here "non-trivial" means:
2538- ///
2539- /// - any allocating operation
2540- /// - any function that is a composition of many operations
2537+ /// If 'f' does not benefit from such a reordering, like `u32::wrapping_add`, prefer the
2538+ /// normal [`Iterator::reduce`] instead since it will most likely result in the generation of
2539+ /// simpler code because the compiler is able to optimize it.
25412540 ///
25422541 /// ```
25432542 /// use itertools::Itertools;
25442543 ///
2544+ /// let f = |a: String, b: String| {
2545+ /// format!("f({a}, {b})")
2546+ /// };
2547+ ///
25452548 /// // The same tree as above
2546- /// let num_strings = (1..8).map(|x| x.to_string());
2547- /// assert_eq!(num_strings.tree_reduce(|x, y| format!("f({}, {})", x, y)),
2548- /// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
2549+ /// assert_eq!((1..8).map(|x| x.to_string()).tree_reduce(f),
2550+ /// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
25492551 ///
25502552 /// // Like fold1, an empty iterator produces None
25512553 /// assert_eq!((0..0).tree_reduce(|x, y| x * y), None);
25522554 ///
25532555 /// // tree_reduce matches fold1 for associative operations...
25542556 /// assert_eq!((0..10).tree_reduce(|x, y| x + y),
25552557 /// (0..10).fold1(|x, y| x + y));
2558+ ///
25562559 /// // ...but not for non-associative ones
25572560 /// assert_ne!((0..10).tree_reduce(|x, y| x - y),
25582561 /// (0..10).fold1(|x, y| x - y));
2562+ ///
2563+ ///
2564+ /// let mut total_capacity_reduce = 0;
2565+ /// let reduce_res = (1..100).map(|x| x.to_string())
2566+ /// .reduce(|a, b| {
2567+ /// let r = f(a, b);
2568+ /// total_capacity_reduce += r.capacity();
2569+ /// r
2570+ /// })
2571+ /// .unwrap();
2572+ ///
2573+ /// let mut total_capacity_tree_reduce = 0;
2574+ /// let tree_reduce_res = (1..100).map(|x| x.to_string())
2575+ /// .tree_reduce(|a, b| {
2576+ /// let r = f(a, b);
2577+ /// total_capacity_tree_reduce += r.capacity();
2578+ /// r
2579+ /// })
2580+ /// .unwrap();
2581+ ///
2582+ /// dbg!(total_capacity_reduce, total_capacity_tree_reduce,
2583+ /// reduce_res.len(), tree_reduce_res.len());
2584+ /// // total_capacity_reduce = 65630
2585+ /// // total_capacity_tree_reduce = 7284
2586+ /// // reduce_res.len() = 679
2587+ /// // tree_reduce_res.len() = 679
25592588 /// ```
25602589 fn tree_reduce < F > ( mut self , mut f : F ) -> Option < Self :: Item >
25612590 where
0 commit comments