std :: for_each работает с несколькими диапазонами итераторов - PullRequest
11 голосов
/ 02 апреля 2012

Лямбда-нотация сделала алгоритмы stl более доступными.Я все еще учусь решать, когда это полезно, а когда вернуться к старым добрым петлям.Часто возникает необходимость перебора двух (или более) контейнеров одинакового размера, так чтобы соответствующие элементы были связаны, но по какой-то причине не упакованы в один класс.

Функция, использующаяцикл для достижения этого выглядел бы так:

template<typename Data, typename Property>
void foo(vector<Data>& data, vector<Property>& prop) {
    auto i_data = begin(data);
    auto i_prop = begin(prop);
    for (; i_data != data.end(); ++i_data, ++i_prop) {
        if (i_prop->SomePropertySatistfied()) {
            i_data->DoSomething();
        }
    }
}

Чтобы использовать for_each, мне нужна его версия, которая обрабатывает несколько диапазонов;что-то вроде:

template<typename InputIter1, typename InputIter2, typename Function>
Function for_each_on_two_ranges(InputIter1 first1, InputIter1 last1, InputIter2 first2, Function f) {
    for (; first1 != last1; ++first1, ++first2) {
        f(*first1, *first2);
    }
    return f;
}

В этой версии приведенный выше код будет выглядеть следующим образом:

template<typename Data, typename Property>
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) {
    for_each_on_two_ranges(begin(data), end(data), begin(prop), [](Data& d, Property& p) {
        if (p.SomePropertySatistfied()) {
            d.DoSomething();
        }
    });
}

Существует ли эквивалентный способ достижения того же результата с использованием алгоритмов stl?

РЕДАКТИРОВАТЬ

Я нашел точный ответ на мой вопрос в виде boost :: for_each, работающего на boost :: range.Я добавил ответ с примером кода для полноты.

Ответы [ 3 ]

8 голосов
/ 05 мая 2012

1) Алгоритмы в STL не предназначены для охвата всех возможных случаев. Если вам нужно for_each_on_two_ranges, напишите его (как у вас) и используйте его.Прелесть STL в том, что он настолько расширяемый, и вы добавили в него новый полезный алгоритм.

2) Если это не сработает, вам не нужно использовать старые добрые циклы for., вместо этого вы можете использовать новые модные циклы for!

Как сказал другой ответ, boost::zip_iterator ваш друг, но его не должно быть сложно использовать.Вот решение, использующее адаптер диапазона, который реализован с zip_iterator

template<typename Data, typename Property>
void foo(vector<Data>& data, vector<Property>& prop) {
    for (auto i : redi::zip(data, prop))
        if (i.get<1>().SomePropertySatistfied())
            i.get<0>.DoSomething();
}

Эта функция zip создает адаптер с элементами begin() и end(), которые возвращают boost::zip_iteratorТаким образом, переменная цикла является кортежем элементов каждого нижележащего контейнера (и, поскольку это шаблон с переменным числом аргументов, вы можете сделать это для любого количества контейнеров, поэтому вам не нужно писать for_each_for_three_ranges и for_each_for_four_ranges и т. д.)

Вы также можете использовать его с for_each

auto z = redi::zip(data, prop);
typedef decltype(z)::iterator::reference reference;

for_each(begin(z), end(z), [](reference i) {
    if (i.get<1>().SomePropertySatistfied()) {
        i.get<0>().DoSomething();
    }
});
5 голосов
/ 08 мая 2012

После прочтения на boost :: zip_iterator и boost :: iterator_range, как было предложено в некоторых ответах, я наткнулся на алгоритмы расширения в boost :: range и нашел точную параллель алгоритма, который я написал для двух диапазонов, но с диапазоном усиления.

Рабочий код для примера будет

#include <boost/range/algorithm_ext/for_each.hpp>

template<typename Data, typename Property>
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) {
    auto rng1 = boost::make_iterator_range(data.begin(), data.end());
    auto rng2 = boost::make_iterator_range(prop.begin(), prop.end());
    boost::for_each(rng1, rng2, [](Data& d, Property& p) {
        if (p.SomePropertySatistfied()) {
            d.DoSomething();
        }
    });
}

Некоторые обертки и служебные функции, схожие с тем, что предлагал @Jonathan Wakely, могут сделать это еще более удобным.

1 голос
/ 02 апреля 2012

std::transform имеет перегрузку, которая работает в двух последовательных параллелях. Вам понадобится нулевой выходной итератор, чтобы получить результаты, если бы вы не были заинтересованы в сборе.

...