C++ boost or STL `y += f(x)` type algorithm
I know I can do this y[i] += f(x[i])
using transform with two input iterators.
however it seems somewhat counterintuitive and more complicated than for loop.
Is there a more natural way to do so using existing algorithm in boost or Stl. I could not find clean equivalent.
here is transform (y = y + a*x):
using boost::lambda;
transform(y.begin(), y.end(), x.begin(), y.begin(), (_1 + scale*_2);
// I thought something may exist:
transform2(x.begin(), x.end(), y.begin(), (_2 + scale*_1);
// it does not, so no 开发者_如何学JAVAbiggie. I will write wrapper
Thanks
There are several ways to do this.
As you noted you can use transform
with a number of predicates, some more or less automatically generated:
std::vector<X> x = /**/;
std::vector<Y> y = /**/;
assert(x.size() == y.size());
//
// STL-way
//
struct Predicate: std::binary_function<X,Y,Y>
{
Y operator()(X lhs, Y rhs) const { return rhs + f(lhs); }
};
std::transform(x.begin(), x.end(), y.begin(), y.begin(), Predicate());
//
// C++0x way
//
std::transform(x.begin(), x.end(), y.begin(), y.begin(),
[](X lhs, Y rhs) { return rhs += f(lhs); });
Now, if we had a vector
with the range of indices, we could do it in a more "pythony" way:
std::vector<size_t> indices = /**/;
//
// STL-way
//
class Predicate: public std::unary_function<size_t, void>
{
public:
Predicate(const std::vector<X>& x, std::vector<Y>& y): mX(x), mY(y) {}
void operator()(size_t i) const { y.at(i) += f(x.at(i)); }
private:
const std::vector<X>& mX;
std::vector<Y>& mY;
};
std::foreach(indices.begin(), indices.end(), Predicate(x,y));
//
// C++0x way
//
std::foreach(indices.begin(), indices.end(), [&](size_t i) { y.at(i) += f(x.at(i)); });
//
// Boost way
//
BOOST_FOREACH(size_t i, indices) y.at(i) += f(x.at(i));
I don't know if there could be something to do with views, they normally allow some pretty syntax. Of course it's a bit difficult here I think because of the self-modifying y
.
Disclaimer: I have no practical experience with valarray, so please don't take this answer as an "advice", but more as a "request for comments". In particular, I have no idea of how efficient this would be. But I'm curious as the notation seems pretty intuitive to me:
With x and y being valarray<int>
and with a function int f(int)
, would:
y += x.apply(&f);
do what you want?
What is wrong with a simple loop?
for (size_t i = 0; i < n; ++i)
y[i] += f(x[i]);
In general even in Fortran it would be:
forall(i=0:n) y(i) += f(x(i))
Though with restrictions on f
, x
, y
it could be written as:
y += f(x)
transform()
variant is more generic and verbose:
std::transform(boost::begin(y), boost::end(y), boost::begin(x),
boost::begin(y), _1 += bind(f, _2));
It might be possible to write zip()
using boost::zip_iterator
:
foreach (auto v, zip(y, z))
v.get<0>() += f(v.get<1>());
where foreach
is BOOST_FOREACH
.
Here's variant similar to @Matthieu M.'s indices:
foreach (size_t i, range(n)) // useless compared to simple loop
y[i] += f(x[i]);
Possible range()
Implementation
template<class T, class T2>
std::pair<boost::counting_iterator<T>,
boost::counting_iterator<T> >
range(T first, T2 last) {
return std::make_pair(boost::counting_iterator<T>(first),
boost::counting_iterator<T>(last));
}
template<class T>
std::pair<boost::counting_iterator<T>,
boost::counting_iterator<T> >
range(T last) {
return range<T>(0, last);
}
Draft (broken) zip()
Implementation
template<class Range1, class Range2>
struct zip_return_type {
typedef boost::tuple<
typename boost::range_iterator<Range1>::type,
typename boost::range_iterator<Range2>::type> tuple_t;
typedef std::pair<
boost::zip_iterator<tuple_t>,
boost::zip_iterator<tuple_t> > type;
};
template<class Range1, class Range2>
typename zip_return_type<Range1, Range2>::type
zip(Range1 r1, Range2 r2) {
return std::make_pair(
boost::make_zip_iterator(
boost::make_tuple(boost::begin(r1), boost::begin(r2))),
boost::make_zip_iterator(
boost::make_tuple(boost::end(r1), boost::end(r2))));
}
You have two ways. I suppose y
is some kind of your own container following the iterator
idea.
The first way is to write another routine that takes y
, x
and some functor as a param. Generally it would do the same y[i] += f(x[i])
stuff, but if you name it correctly, this would make your code cleaner and easier to understand.
Another way is operator +=
(or +
, or better together) overloading so that (let's say y
has a container
type) it would look the following way:
container& operator+ (functor_type& functor)
Here your functor should be a struct / class declared the following way:
class functor {
private:
container c;
public:
functor (container& c) : c(c) { }
container operator() (void) { (...) - your actions on container here }
};
This way you could write y += f(x)
and it would be ok. However, I wouldn't recommend this way of managing your code, because all these operator overloads on your own datatypes generally make the code harder to understand.
精彩评论