开发者

C++ boost or STL `y += f(x)` type algorithm

开发者 https://www.devze.com 2022-12-28 13:52 出处:网络
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.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消