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}