Reputation: 22552
Is there any efficient and idiomatic way to perform the following operation?
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
for (std::size_t i = 0 ; i < a.size() ; ++i)
{
a[i] += b[i];
}
I am trying to avoid the brackets/index notation and only use iterators in order for the operation to work with any container with forward iterators. I thought of the following solution:
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
std::transform(a.begin(), a.end(),
b.begin(),
a.begin(),
std::plus<int>());
However, there is the redundancy of a.begin()
and I only get to use +
and not +=
. Is there some algorithm in the standard library that would allow me to use iterators without having any redundancy or do I have to write the full loops by hand?
Upvotes: 8
Views: 2986
Reputation: 22552
I couldn't find the kind of generic function I was looking for and finally went with the following function that I named range_map
("map the given function element-wise with two given ranges"). As the comments point, it is actually no more than a binary std::for_each
:
template<class InputIt1, class InputIt2, class BinaryOperation>
void range_map(InputIt1 first1, InputIt1 last1,
InputIt2 first2, BinaryOperation binary_op)
{
while (first1 != last1) {
binary_op(*first1++, *first2++);
}
}
I created the class plus_assign
the following way:
template<typename T>
struct plus_assign
{
void operator()(T &lhs, const T &rhs) const
{
lhs += rhs;
}
};
And then my code becomes:
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
range_map(a.begin(), a.end(),
b.begin(),
plus_assign<int>());
There is also the unary counterpart of the function range_map
, to map a given functor to a range:
template<class InputIt, class BinaryOperation>
void range_map(InputIt first, InputIt last,
UnaryOperation unary_op)
{
while (first != last) {
unary_op(*first1++);
}
}
Upvotes: 1
Reputation: 1594
Use operator overload
#include <vector>
std::vector<int>& operator += (std::vector<int>& a, std::vector<int> const& b)
{
for(size_t i = 0; i != a.size(); ++i)
a[i] += b[i];
return a;
}
int main(int argc, char * argv[])
{
std::vector<int> a { 1, 3, 5, 7, 9};
std::vector<int> b { 2, 4, 6, 8, 10};
a += b;
return 0;
}
Upvotes: 0
Reputation: 42072
If you were to use this more than once, and you were interested in a simple interface in the spirit of the Standard Library, you could create a simple template class for the specific use case (which I refer to as "Range Increment"), and write something like:
#include<vector>
#include<algorithm>
#include<iostream>
template<typename InputIt>
InputIt range_increment(InputIt dbeg, InputIt dend, InputIt sbeg) {
while(dbeg!=dend) {
*(dbeg++) += (*sbeg++);
}
return dbeg;
}
int main() {
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<int> b = { 5, 6, 7, 8 };
range_increment(a.begin(), a.end(), b.begin());
for(auto x:a) {
std::cout<<x<<std::endl;
}
}
Which yields:
6
8
10
12
Upvotes: 4
Reputation: 490098
Perhaps something that was intended to become idiomatic, but never quite did:
std::valarray<int> a = { 1, 2, 3, 4 };
std::valarray<int> b = { 5, 6, 7, 8 };
Now you can do these
std::valarray<int> c = a + b; //apply '+' element-wise; store result in c
a += b; //apply '+=' element-wise
See the documentation of std::valarray for more details.
Upvotes: 9
Reputation: 54270
What's wrong with the redundancy of a.begin()
?
If you're not happy with it, just invent your own algorithm: transform_inplace
template <class InputIterator, class OutputIterator, class BinaryOperator>
OutputIterator transform_inplace (InputIterator first,
InputIterator last,
OutputIterator result,
BinaryOperator op)
{
while (first != last) {
*result = op(*result, *first);
++result;
++first;
}
return result;
}
Upvotes: 4
Reputation: 3026
Not sure I'd call it "idiomatic", but:
assert(a.size()==b.size());
auto bi = b.begin();
for (auto& i : a) {
i += *(bi++);
}
is pretty concise.
Upvotes: 2