Skip to content

Commit 0d879ed

Browse files
committed
Added fold, foreach, sum; with docs and tests
1 parent a08c543 commit 0d879ed

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

inc/aggregators.hpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,123 @@ template <typename T> size_t count(generator<T> &gen) {
8383
}
8484
return cnt;
8585
}
86+
87+
/**
88+
* \brief Accumulates each value in the generator using the provided function.
89+
*
90+
* For each element in the generator, the provided function (which should have
91+
* (TOut, TIn) -> TOut) as signature is called. The result is stored in the
92+
* accumulator, which is passed down to the next value in the generator. Once
93+
* all values are extracted, the resulting accumulator is returned. The
94+
* accumulator is initialized using `TOut value = {};`.
95+
*
96+
* \tparam TOut The output type (accumulator type).
97+
* \tparam TIn The input type (type contained in the generator).
98+
* \tparam Fun The function type (should have the signature (TOut, TIn) ->
99+
* TOut).
100+
* \param[in,out] gen The generator to fold.
101+
* \param[in] folder The folding function.
102+
* \returns The final accumulator value.
103+
*/
104+
template <typename TOut, typename TIn, typename Fun>
105+
TOut fold(generator<TIn> &gen, Fun folder) {
106+
TOut value = {};
107+
while (gen) {
108+
value = folder(value, gen());
109+
}
110+
return value;
111+
}
112+
113+
/**
114+
* \brief Accumulates each value in the generator using the provided function.
115+
*
116+
* For each element in the generator, the provided function (which should have
117+
* (TOut, TIn) -> TOut) as signature is called. The result is stored in the
118+
* accumulator, which is passed down to the next value in the generator. Once
119+
* all values are extracted, the resulting accumulator is returned. The
120+
* accumulator is initialized using `TOut value(initial);`.
121+
*
122+
* \tparam TOut The output type (accumulator type).
123+
* \tparam TIn The input type (type contained in the generator).
124+
* \tparam Fun The function type (should have the signature (TOut, TIn) ->
125+
* TOut).
126+
* \param[in,out] gen The generator to fold.
127+
* \param[in] folder The folding function.
128+
* \param[in] initial The initial value for the accumulator (is copied).
129+
* \returns The final accumulator value.
130+
*/
131+
template <typename TOut, typename TIn, typename Fun>
132+
TOut fold(generator<TIn> &gen, Fun folder, TOut initial) {
133+
TOut value(initial);
134+
while (gen) {
135+
value = folder(value, gen());
136+
}
137+
return value;
138+
}
139+
140+
/**
141+
* \brief Accumulates each value in the generator using the provided function.
142+
*
143+
* For each element in the generator, the provided function (which should have
144+
* (TOut, TIn) -> TOut) as signature is called. The result is stored in the
145+
* provided accumulator, which is passed down to the next value in the
146+
* generator. Once all values are extracted, the resulting accumulator is
147+
* returned. Each step modifies the accumulator.
148+
*
149+
* \tparam TOut The output type (accumulator type).
150+
* \tparam TIn The input type (type contained in the generator).
151+
* \tparam Fun The function type (should have the signature (TOut, TIn) ->
152+
* TOut or (TOut &, TIn) -> TOut).
153+
* \param[in,out] gen The generator to fold.
154+
* \param[in] folder The folding function.
155+
* \param[out] initial The initial value for the accumulator (is overwritten).
156+
* \returns A reference to the value which was passed as initial value and is
157+
* now the output value.
158+
*/
159+
template <typename TOut, typename TIn, typename Fun>
160+
TOut &fold_ref(generator<TIn> &gen, Fun folder, TOut &initial) {
161+
while (gen) {
162+
initial = folder(initial, gen());
163+
}
164+
return initial;
165+
}
166+
167+
/**
168+
* \brief Sums each value in the generator.
169+
*
170+
* The type contained in the generator should support `operator+`. An initial
171+
* accumulator is constructed using `T accum = {}`. Each next value is added to
172+
* the accumulator, which is returned afterwards.
173+
*
174+
* \tparam T The type contained in the generator, should support `operator+`.
175+
* \param[in,out] gen The generator to sum over.
176+
* \returns The sum of all elements.
177+
*/
178+
template <typename T> T sum(generator<T> &gen) {
179+
T accum = {};
180+
while (gen) {
181+
accum = accum + gen();
182+
}
183+
return accum;
184+
}
185+
186+
/**
187+
* \brief Loops over the generator, calling a function on each element in it.
188+
*
189+
* Each element is extracted from the generator and used only as function
190+
* argument. All other aggregators can be written as a combination of this
191+
* function and a lambda function. Afterwards, the generator will be empty.
192+
*
193+
* \tparam T The type of values contained in the generator.
194+
* \tparam Fun The function type of the callback.
195+
* \param[in,out] gen The generator to iterate over.
196+
* \param[in] func The function to use.
197+
*/
198+
template <typename T, typename Fun> void foreach (generator<T> &gen, Fun func) {
199+
while (gen) {
200+
func(gen());
201+
}
202+
}
86203
} // namespace fpgen
87204

88205
#endif

test/src/test_aggreg.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@ fpgen::generator<size_t> values() {
2525
co_return i + j;
2626
}
2727

28+
size_t calc_sum() {
29+
size_t i = 0;
30+
size_t j = 1;
31+
size_t sum = 1;
32+
while (j <= 21) {
33+
size_t tmp = i + j;
34+
sum += tmp;
35+
i = j;
36+
j = tmp;
37+
}
38+
return sum;
39+
}
40+
41+
size_t sum(size_t old, size_t in) { return old + in; }
42+
size_t sum_ref(size_t &old, size_t in) {
43+
old = old + in;
44+
return old;
45+
}
46+
2847
TEST(aggregate, empty) {
2948
auto gen = a_empty();
3049
gen();
@@ -78,3 +97,67 @@ TEST(aggregate, count) {
7897
auto gen = values();
7998
EXPECT_EQ(10, fpgen::count(gen));
8099
}
100+
101+
TEST(fold, fold_noin_empty) {
102+
auto gen = a_empty();
103+
gen();
104+
EXPECT_EQ(0, fpgen::fold<size_t>(gen, sum));
105+
}
106+
107+
TEST(fold, fold_noin) {
108+
auto gen = values();
109+
EXPECT_EQ(calc_sum(), fpgen::fold<size_t>(gen, sum));
110+
}
111+
112+
TEST(fold, fold_in_noref_empty) {
113+
auto gen = a_empty();
114+
gen();
115+
EXPECT_EQ(7, fpgen::fold<size_t>(gen, sum, 7));
116+
}
117+
118+
TEST(fold, fold_in_noref) {
119+
auto gen = values();
120+
EXPECT_EQ(calc_sum() + 7, fpgen::fold<size_t>(gen, sum, 7));
121+
}
122+
123+
TEST(fold, fold_in_ref_empty) {
124+
auto gen = a_empty();
125+
gen();
126+
size_t res = 7;
127+
EXPECT_EQ(7, fpgen::fold_ref<size_t>(gen, sum, res));
128+
EXPECT_EQ(7, res);
129+
}
130+
131+
TEST(fold, fold_in_ref) {
132+
auto gen = values();
133+
size_t res = 7;
134+
EXPECT_EQ(calc_sum() + 7, fpgen::fold_ref<size_t>(gen, sum, res));
135+
EXPECT_EQ(calc_sum() + 7, res);
136+
}
137+
138+
TEST(sum, empty) {
139+
auto gen = a_empty();
140+
gen();
141+
EXPECT_EQ(0, fpgen::sum(gen));
142+
}
143+
144+
TEST(sum, normal) {
145+
auto gen = values();
146+
EXPECT_EQ(calc_sum(), fpgen::sum(gen));
147+
}
148+
149+
TEST(foreach, empty) {
150+
auto gen = a_empty();
151+
gen();
152+
size_t res = 0;
153+
fpgen::foreach (gen, [&res](size_t val) { res += val; });
154+
EXPECT_EQ(res, 0);
155+
}
156+
157+
TEST(foreach, normal) {
158+
auto gen = values();
159+
auto gen2 = values();
160+
size_t res = 0;
161+
fpgen::foreach (gen, [&res](size_t val) { res += val; });
162+
EXPECT_EQ(res, fpgen::sum(gen2));
163+
}

0 commit comments

Comments
 (0)