Reputation: 9632
I have read some questions that are similar but I don't find the exact thing I am looking for.
In a purely mathematical way a list is defined recursively as: (head, rest)
.
Where head
is the first element in the list and rest
is a list.
So for example (1,2,3,4) is represented as (1, (2,(3,(4,[])))) where []
is the empty list.
Then if we want to iterate over the list we can write a recursive function more or less like this:
iterate(list)
head = list.head
// do stuff and return if head is the empty element
iterate(list.rest)
And if we want to iterate over every 2 elements we do:
pair_iterate(list)
head1 = list.head
head2 = list.rest.head
// do stuff and return if head is the empty element
iterate(list.rest.rest)
I am trying to get that second behaviour in C++.
In C++ 17, folds got introduced, so one can do something like this:
template<typename...types>
auto sum(types...values) {
return (... + values);
}
But let's say we wanted the sum of the products of adjacent parameters, e.g sum(1,2,3,4)
is 1*2 + 3*4
.
In this case we need to "fold twice" to get the the 2 heads to perform the operation and pass the rest of the list. Similar to my pseudocode.
Does anyone have advice on how to get 2 folds in a row?
EDIT: I specifically want to do it with folds, i.e inside the function declaration without having to rely on recursive templated functions.
Upvotes: 1
Views: 268
Reputation: 218323
Easier would be to change prototype to take pairs:
template <typename ...Pairs>
auto sum_products(Pairs ...ts)
{
return (... + (ts.first * ts.second));
}
else a generic solution is to use index_sequence
:
template <std::size_t... Is, typename T>
auto sum_products_impl(std::index_sequence<Is...>, const T& t)
{
return (... + (std::get<2 * Is>(t) * std::get<2 * Is + 1>(t)));
}
template <typename ...Ts>
auto sum_products(Ts ...ts)
{
static_assert(sizeof...(Ts) % 2 == 0);
return sum_products_impl(std::make_index_sequence<sizeof...(Ts) / 2>(), std::tie(ts...));
}
Upvotes: 0
Reputation: 60440
You can unpack the parameters 2 at a time, like this:
template <typename T1, typename T2, typename ...Ts>
auto sum_products(T1 t1, T2 t2, Ts ...ts)
{
return t1 * t2 + sum_products(ts...);
}
and provide a base case overload for no arguments:
auto sum_products() { return 0; }
and then use it like this:
std::cout << sum_products(1,2,3,4); // prints 14
Here's a demo.
Note that this will only work for an even number of arguments, but you can easily add a single argument overload to handle that case.
Upvotes: 2