Ranges provide a beautiful solution for this case:
- get the input vector,
[1, 2, 3, 4, 5]
- repeat it indefinitely with
repeat
,
[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], ...]
- flatten the new view out with
join
,
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...]
- take as many elements as the vector size plus one with
take
, and
[1, 2, 3, 4, 5, 1]
- apply a transformation to each adjacent pair with
adjacent_transform<2>
.
[[1, 2], [2, 3], ...] -> [f(1,2), f(2,3), ...]
Notice repeat
and adjacent_transform
will be available in C++23.
join
and take
should be available in C++20.
[Demo]
#include <fmt/ranges.h>
#include <functional> // multiplies, plus
#include <ranges>
#include <vector>
template <typename C, typename F>
auto my_adjacent_transform(C&& c, F&& f) {
return std::views::repeat(std::forward<C>(c))
| std::views::join
| std::views::take(c.size() + 1)
| std::views::adjacent_transform<2>(f);
}
int main() {
std::vector<int> v{ 1, 2, 3, 4, 5 };
fmt::print("v: {}\n", v);
fmt::print("Adding pairs: {}\n", my_adjacent_transform(v, std::plus<>{}));
fmt::print("Multiplying pairs: {}\n", my_adjacent_transform(
std::vector<int>{ 1, 2, 3, 4, 5 }, std::multiplies<>{}));
}
// Outputs:
//
// v: [1, 2, 3, 4, 5]
// Adding pairs: [3, 5, 7, 9, 6]
// Multiplying pairs: [2, 6, 12, 20, 5]
Alternatively, you could already use Eric Niebler's range-v3 library, and the solution would be quite similar:
- get the input vector,
[1, 2, 3, 4, 5]
- repeat its contents indefinitely with
cycle
,
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...]
- take as many elements as the vector size plus one with
take
,
[1, 2, 3, 4, 5, 1]
- create a view of adjacent pairs with
sliding(2)
, and
[[1, 2], [2, 3], ...]
- apply a transformation to each pair with
transform
.
[t(1, 2), t(2, 3), ...]
Notice from the example below that range-v3 library lets you construct a container from a view via ranges::to
. This conversion function will also be part of C++23.
[Demo]
#include <fmt/ranges.h>
#include <functional> // multiplies, plus
#include <range/v3/all.hpp>
#include <vector>
template <typename C, typename F>
auto my_adjacent_transform(C&& c, F&& f) {
auto t = [&f](auto&& p) { return std::forward<F>(f)(p[0], p[1]); };
return std::forward<C>(c)
| ranges::views::cycle
| ranges::views::take(c.size() + 1)
| ranges::views::sliding(2)
| ranges::views::transform(t);
}
int main() {
std::vector<int> v{ 1, 2, 3, 4, 5 };
fmt::print("v: {}\n", v);
fmt::print("Adding pairs: {}\n", my_adjacent_transform(v, std::plus<>{}));
auto w{ my_adjacent_transform(v, std::multiplies<>{})
| ranges::to<std::vector<int>>() };
fmt::print("Multiplying pairs: {}\n", w);
}