Imagine I have an ordered std::vector A = {x1, x2, ..., xn}
and I want to perform an operation on every subsequent pair of items, e.g. f(x1, x2); f(x2, x3); ... f(xn-1, xn); f(xn, x1)
.
I could iterate like I normally would, while tracking the previous item:
for (auto x : A) {
...
f(previous_x, x);
previous_x = x;
}
f(previous_x, first_x);
But is there a better way to iterate through this vector? Are there features in the language that can streamline this?
Tried the solution provided. It works, but curious to know if there is a cleaner and more concise way.
CodePudding user response:
Here you are
std::vector<int> v = { 1, 2, 3, 4, 5 };
for (std::vector<int>::size_type i = 0, n = std::size( v ); i < n; i )
{
std::cout << v[i] v[( i 1 ) % n] << ' ';
}
std::cout << '\n';
the output of this code snippet is
3 5 7 9 6
You can use a similar approach.
Before the for loop you can check whether a vector contains at least two elements.
CodePudding user response:
You could use an old-school non range based for-loop and dereference the current next modulus A.size()
iterator:
#include <iostream>
#include <vector>
void foo(int a, int b) { std::cout << a << ',' << b << '\n'; }
int main() {
std::vector<int> A{1, 2, 3};
if (A.size() >= 2) {
for (auto it = A.begin(); it != A.end(); it) {
foo(*it, *std::next(A.begin(),
(std::distance(A.begin(), it) 1) % A.size()));
}
}
}
Output:
1,2
2,3
3,1
Or... use indices to do the same thing, like @Vlad showed.
CodePudding user response:
Ranges provide a beautiful solution for this case:
- get the input vector (
12345
), - repeat it indefinitely (
12345123451...
), - take as many elements as the vector size plus one (
123451
), - create a vector of pairs by starting with a window of the first two elements and sliding it to the end (
12
,23
...,51
), and, finally, - apply a transformation to each pair.
I have used Eric Niebler's range-v3 library, because std::views::cycle
hasn't made it yet into C 23 ranges.
#include <fmt/ranges.h>
#include <functional> // multiplies, plus
#include <range/v3/all.hpp>
#include <vector>
template <typename C, typename F>
auto my_adjacent_transform(const C& c, F&& f) {
return c
| ranges::views::cycle
| ranges::views::take(c.size() 1)
| ranges::views::sliding(2)
| ranges::views::transform([&f](auto&& p) {
return std::forward<F>(f)(p[0], p[1]);
});
}
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);
}
// Outputs:
//
// v: [1, 2, 3, 4, 5]
// Adding pairs: [3, 5, 7, 9, 6]
// Multiplying pairs: [2, 6, 12, 20, 5]
CodePudding user response:
Just do it:
A.push_back(A[0]); // copy first element to end
for (int j = 0; j < A.size() - 1; j)
f(A[j], A[j 1]);