Let's assume that I want to calculate subtraction for elements in a container, as:
result = i1 - i2 - i3 - ... - in
Using std::accumulate
this can be done as in the following code:
std::vector v {10, 2, 3};
// skip the first element and use it as the init value
auto result =
accumulate(v.begin() + 1, v.end(), v.front(),
[](double d1, double d2) { return d1 - d2; }); // 5
The need to manually retrieve front
as init is reasonable, I'd say, as there is no code or logic duplication in that.
But if there is a need to provide the UnaryOperator this becomes a bit more cumbersome and requires some code duplication. Let's assume the values are not numbers, they are wrapped inside "Expressions" that need to be evaluated.
Using std::transform_reduce
:
// skip the first element and evaluate it, to use it as the init value
auto result =
transform_reduce(expressions.begin() + 1, expressions.end(),
expressions.front()->eval(),
[this](double d1, double d2) {
return eval(d1, d2);
},
[this](const Expression* e) {
return e->eval();
} );
Is there any alternative, allowing for something like (pseudo-code):
// if init is not provided use the UnaryOp and the first element for init
auto result =
transform_reduce(subexpressions.begin(), subexpressions.end(),
[this](double d1, double d2) {
return eval(d1, d2);
},
[this](const Expression* e) {
return e->eval();
}
);
I can of course implement my own function for that, e.g. below, but want to first check I'm not missing an existing algorithm.
Here is what I'm looking for (actual name can be just transform_reduce as an additional overload):
template<typename InputIt, typename UnaryOp, typename BinaryOp>
auto transform_reduce_init(InputIt first, InputIt last,
BinaryOp binary_op, UnaryOp unary_op) {
// Use the first element as the initial value
auto init = unary_op(*first++);
// Perform the transformation and reduction
return std::transform_reduce(first, last, init, binary_op, unary_op);
}
You can use ranges::fold_left_first
, which returns a std::optional
(to accommodate the case where the range is empty)
std::optional result = std::ranges::fold_left_first(
v | std::views::transform([](const auto& elem) { return elem->eval(); }),
[&](auto lhs, auto rhs) { return eval(lhs, rhs); }
);
if (result) {
// ...
}