My struct is a 2D coordinate:
template<typename T>
struct coordinate {
std::pair<T, T> coords;
coordinate() : coords({0, 0}) {}
const T x() const { return coords.first; }
const T y() const { return coords.second; }
// functions to set and manipulate x and y
};
I have a std::vector<coordinate<double>> vec
and would like to get averages of x and of y coordinates across the vector.
My way is (please don't judge)
double x_ = 0.;
double y_ = 0.;
for (auto v : vec) {
x_ = v.x();
y_ = v.y();
}
x_ /= vec.size();
y_ /= vec.size();
I assume there is a more suitable approach? I try to go for std::accumulate
, but don't know how to access the struct members in std::accumulate
.
I still struggle with C so some explanation to your approach would be great.
CodePudding user response:
Like I said, if you want to do arithmetic operations on your type, you likely want to overload the operators. Thus you can do
#include <numeric>
#include <vector>
template<typename T>
struct coordinate {
std::pair<T, T> coords;
coordinate() : coords({0, 0}) {}
coordinate(T x,T y) : coords(x,y) {}
T x() const { return coords.first; }
T y() const { return coords.second; }
coordinate& operator =(coordinate const& other){
coords.first = other.coords.first;
coords.second = other.coords.second;
return *this;
}
template<typename D>
coordinate& operator/=(D divider){
coords.first /= divider;
coords.second /= divider;
return *this;
}
};
template<typename T>
coordinate<T> operator (coordinate<T> lhs, coordinate<T> const& rhs){
return lhs = rhs;
}
template<typename T, typename D>
coordinate<T> operator/(coordinate<T> lhs, D rhs){
return lhs /= rhs;
}
template<typename T>
coordinate<T> average(std::vector<coordinate<T>> const& vec){
if (vec.empty()){
return coordinate<T>{};
}
return std::accumulate(cbegin(vec), cend(vec), coordinate<T>{}) / vec.size();
}
int main()
{
std::vector<coordinate<double>> vec{{1,1},{2,0}};
auto avg = average(vec);
}
CodePudding user response:
std::accumulate
takes a BinaryOperation that adds one element to the accumulator:
#include <iostream>
#include <vector>
#include <numeric>
template<typename T>
struct coordinate {
std::pair<T, T> coords;
coordinate() : coords({0, 0}) {}
coordinate(T x,T y) : coords(x,y) {}
const T x() const { return coords.first; }
const T y() const { return coords.second; }
};
int main()
{
std::vector<coordinate<double>> vec{{1,1},{2,0}};
auto avg = std::accumulate(vec.begin(),vec.end(),coordinate{0,0},[](const auto& a,const auto& b){
auto ret = a;
ret.coords.first = b.coords.first;
ret.coords.second = b.coords.second;
return ret;
});
std::cout << avg.coords.first << " " << avg.coords.second;
}
However, this isnt much simpler than your loop. Things are different when you define a operator
because thats what accumulate
uses by default. Then you could simply write std::accumulate(vec.begin(),vec.end(),coords(0,0));
.
Note that I had to add a constructor for the example and you still have to divide by vec.size
, perhaps also via a operator/=
.
if my vector would only have doubles, then yes. But I don't get, how to use accumulate, if my vector has structs instead doubles.
If T
is some struct then that struct should implement an operator
or operator =
. If the struct does not have either it does not make sense to average it.