C ++ Boost или STL `y + = f (x)` тип алгоритма - PullRequest
2 голосов
/ 30 апреля 2010

Я знаю, что могу сделать это y[i] += f(x[i]), используя преобразование с двумя входными итераторами. однако это кажется несколько нелогичным и более сложным, чем для цикла.

Есть ли более естественный способ сделать это, используя существующий алгоритм в boost или Stl. Я не мог найти чистый эквивалент.

вот преобразование (у = у + а * х):

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 biggie. I will write wrapper

Спасибо

Ответы [ 4 ]

7 голосов
/ 30 апреля 2010

Есть несколько способов сделать это.

Как вы заметили, вы можете использовать transform с несколькими предикатами, некоторые более или менее автоматически генерируются:

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); });

Теперь, если бы у нас было vector с диапазоном индексов, мы могли бы сделать это более "питонным" способом:

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));

Я не знаю, можно ли что-то делать с представлениями, они обычно допускают довольно симпатичный синтаксис. Конечно, это немного сложно, я думаю, из-за самоизменения y.

3 голосов
/ 30 апреля 2010

Отказ от ответственности : У меня нет практического опыта работы с valarray, поэтому, пожалуйста, не воспринимайте этот ответ как «совет», а скорее как «запрос комментариев». В частности, я понятия не имею, насколько это будет эффективно. Но мне любопытно, поскольку запись кажется мне довольно интуитивной:

Если x и y равны valarray<int>, а функция int f(int), то будет:

y += x.apply(&f);

делай что хочешь?

1 голос
/ 01 мая 2010

Что не так с простым циклом?

for (size_t i = 0; i < n; ++i)
  y[i] += f(x[i]); 

В общем, даже в Фортране это будет:

forall(i=0:n) y(i) += f(x(i))

Хотя с ограничениями на f, x, y это можно записать как:

y += f(x)

transform() вариант более общий и подробный:

std::transform(boost::begin(y), boost::end(y), boost::begin(x), 
               boost::begin(y), _1 += bind(f, _2)); 

Возможно, можно написать zip(), используя boost::zip_iterator:

foreach (auto v, zip(y, z)) 
  v.get<0>() += f(v.get<1>());

, где foreach равно BOOST_FOREACH.

Вот вариант, аналогичный @ индексам Матье М. :

foreach (size_t i, range(n)) // useless compared to simple loop
  y[i] += f(x[i]); 

Возможно range() Реализация

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);
}

Черновик (ломаная) zip() Реализация

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))));
}
0 голосов
/ 30 апреля 2010

У вас есть два пути. Я полагаю, y - это какой-то ваш собственный контейнер, следующий идее iterator.

Первый способ - написать еще одну подпрограмму, которая принимает y, x и некоторый функтор в качестве параметра. Как правило, он будет делать то же самое y[i] += f(x[i]), но если вы назовете его правильно, это сделает ваш код чище и проще для понимания.

Другим способом является перегрузка оператора += (или +, или лучше вместе), так что (скажем, y имеет тип container) это будет выглядеть следующим образом:

container& operator+ (functor_type& functor)

Здесь ваш функтор должен быть структурой / классом, объявленным следующим образом:

class functor {
private:
   container c;
public:
   functor (container& c) : c(c) { }
   container operator() (void) { (...) - your actions on container here }
};

Таким образом, вы можете написать y += f(x), и все будет в порядке. Однако я не рекомендовал бы такой способ управления вашим кодом, потому что все эти перегрузки операторов в ваших собственных типах данных обычно затрудняют понимание кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...