reductor/
reductor.rs

1/// Reductors are types that implement the logic for [`fold`](Iterator::fold)ing an iterator
2/// into a single result.
3///
4/// ```rust
5/// use reductor::{Reductor, Reduce};
6///
7/// struct MeanState { mean: f32, count: usize };
8///
9/// struct Mean(f32);
10///
11/// impl Reductor<f32> for Mean {
12///     type State = MeanState;
13///     
14///     fn new(item: f32) -> Self::State {
15///         MeanState { mean: item, count: 1 }
16///     }
17///
18///     fn reduce(acc: Self::State, item: f32) -> Self::State {
19///         MeanState {
20///             mean: acc.mean + item,
21///             count: acc.count + 1,
22///         }
23///     }
24///     
25///     fn into_result(state: Self::State) -> Self {
26///         Self(state.mean / state.count as f32)
27///     }
28/// }
29///
30/// let Mean(mean) = vec![8.5, -5.5, 2.0, -4.0].into_iter()
31///     .reduce_with::<Option<Mean>>()
32///     .unwrap();
33///
34/// assert!((mean - 0.25).abs() < f32::EPSILON);
35/// ```
36pub trait Reductor<A>: Sized {
37    /// Intermediate state for the reductor.
38    ///
39    /// This type is used to keep track of the state of reduction while processing
40    /// an iterator. The first item yielded is converted into the `State` type by
41    /// calling [`new`](Reductor::new). The next item will be reduced using the previous
42    /// state by calling [`reduce`](Reductor::reduce) with the new item, and the resulting
43    /// state will be used for the next reduction, and so forth. When the iterator
44    /// is exhausted, the intermediate state will be turned into a result by calling
45    /// [`into_result`](Reductor::into_result).
46    ///
47    /// `State` must implement the [`Default`] trait for the `Reductor` to be used
48    /// with [`reduce_with`](crate::Reduce::reduce_with), otherwise, an initial state
49    /// can be provided by calling [`fold_with`](crate::Reduce::fold_with).
50    type State;
51
52    /// This method will be called with the first item yielded by an iterator
53    /// to create the initial state of the reductor.
54    fn new(item: A) -> Self::State;
55
56    /// Reduce the current accumulated state with the next item yielded by an iterator,
57    /// returning the new state.
58    fn reduce(state: Self::State, item: A) -> Self::State;
59
60    /// After reducing the entire iterator, and exhausting it, turn the final state into
61    /// a result.
62    fn into_result(state: Self::State) -> Self;
63}
64
65/// Wrapping a [`Reductor`] in an [`Option`] allows using [`reduce_with`](crate::Reduce::reduce_with)
66/// with a `Reductor` whose [`State`](Reductor::State) does not implement [`Default`].
67///
68/// ```compile_fail
69/// # use reductor::{Reduce, Min};
70/// let _ = (0..10).reduce_with::<Min<u32>>();
71/// ```
72///
73/// ```rust
74/// # use reductor::{Reduce, Min};
75/// let _ = (0..10).reduce_with::<Option<Min<u32>>>();
76/// ```
77impl<R, A> Reductor<A> for Option<R>
78where
79    R: Reductor<A>,
80{
81    type State = Option<R::State>;
82
83    fn new(item: A) -> Self::State {
84        Some(R::new(item))
85    }
86
87    fn reduce(state: Self::State, item: A) -> Self::State {
88        Some(match state {
89            None => R::new(item),
90            Some(state) => R::reduce(state, item),
91        })
92    }
93
94    fn into_result(state: Self::State) -> Self {
95        state.map(R::into_result)
96    }
97}
98
99/// This struct can be used to run a tuple of [`Reductor`]s on a single value,
100/// by [cloning](`Clone`) every element yielded, and updating all `Reductor`s'
101/// states in each iteration.
102#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
103pub struct Reductors<R>(pub R);
104
105#[rustfmt::skip]
106mod agg_impls {
107    use super::*;
108
109    macro_rules! impl_reductor_for_tuple {
110        ($(#[$meta:meta])* $([$A:ident: $R:ident, $Idx:tt]),+$(,)?) => {
111            $(#[$meta])*
112            impl<$($A),+, $($R),+> Reductor<($($A),+)> for ($($R),+)
113            where
114                $($R: Reductor<$A>),+
115            {
116                type State = ($($R::State),+);
117
118                fn new(item: ($($A),+)) -> Self::State {
119                    ($($R::new(item.$Idx)),+)
120                }
121
122                fn reduce(state: Self::State, item: ($($A),+)) -> Self::State {
123                    ($($R::reduce(state.$Idx, item.$Idx)),+)
124                }
125
126                fn into_result(state: Self::State) -> Self {
127                    ($($R::into_result(state.$Idx)),+)
128                }
129            }
130        };
131    }
132
133    impl_reductor_for_tuple!(
134        /// Two `Reductor`s can be combined in a tuple to reduce iterators that yield two-element tuples.
135        ///
136        /// ```rust
137        /// use reductor::{Reduce, Sum, Product};
138        ///
139        /// let iter = (5..10).map(|x| (x, -(x as i64)));
140        ///
141        /// let (Sum(sum), Product(product)) = iter
142        ///     .clone()
143        ///     .reduce_with::<(Sum<u64>, Product<i64>)>();
144        ///
145        /// assert_eq!(sum, iter.clone().map(|(x, ..)| x).sum());
146        /// assert_eq!(product, iter.clone().map(|(.., x)| x).product())
147        /// ```
148        ///
149        /// See [`Reductors`] for reducing a single-item tuple with multiple `Reductor`s.
150        [A1: R1, 0], [A2: R2, 1],
151    );
152
153    impl_reductor_for_tuple!([A1: R1, 0], [A2: R2, 1], [A3: R3, 2]);
154    impl_reductor_for_tuple!([A1: R1, 0], [A2: R2, 1], [A3: R3, 2], [A4: R4, 3]);
155    impl_reductor_for_tuple!([A1: R1, 0], [A2: R2, 1], [A3: R3, 2], [A4: R4, 3], [A5: R5, 4]);
156
157    macro_rules! impl_reductor_for_reductors {
158        ($(#[$meta:meta])* $([$R:ident, $Idx:tt]),+$(,)?) => {
159            $(#[$meta])*
160            impl<A, $($R),+> Reductor<A> for Reductors<($($R),+)>
161            where
162                A: Clone,
163                $($R: Reductor<A>),+
164            {
165                type State = ($($R::State),+);
166
167                fn new(item: A) -> Self::State {
168                    ($($R::new(item.clone())),+)
169                }
170
171                fn reduce(state: Self::State, item: A) -> Self::State {
172                    ($($R::reduce(state.$Idx, item.clone())),+)
173                }
174
175                fn into_result(state: Self::State) -> Self {
176                    Self(($($R::into_result(state.$Idx)),+))
177                }
178            }
179        };
180    }
181
182    impl_reductor_for_reductors!([R1, 0], [R2, 1]);
183    impl_reductor_for_reductors!([R1, 0], [R2, 1], [R3, 2]);
184    impl_reductor_for_reductors!([R1, 0], [R2, 1], [R3, 2], [R4, 3]);
185    impl_reductor_for_reductors!([R1, 0], [R2, 1], [R3, 2], [R4, 3], [R5, 4]);
186}