Подтвердить что ты не робот

For_each, который дает два (или n) соседних элемента

Существует ли стандартная реализация for_each, которая вызывает элемент и следующий в диапазоне?

Например, возьмите диапазон {0, 1, 2, 3, 4, 5}, я хотел бы вызвать функцию f с каждым элементом и его преемником: {f(0, 1), f(1, 2), f(2, 3), f(3, 4), f(4, 5)} Обратите внимание, что последний элемент пропущен, потому что он не имеет преемника.

Также было бы хорошо, если бы это было обобщено на n преемников, которые передаются вместе с самим элементом.

До сих пор я всегда решал эту проблему с помощью рукописного цикла с итераторами. Тем не менее, я хотел бы пойти намного дальше в соответствии с диапазоном С++ 11, основанным на или std::for_each чтобы избежать кода на уровне котла.

Примеры

// today: handwritten loop
for(Range::iterator current = range.begin(); current != range.end(); ++current) 
   f(*current, *std::next(current));

// near future: wrapped version
for_each_pair(range.begin(), range.end(), f);

// slightly further future: generalized version
for_each_tuple<n>(range.begin(), range.end(), f);

Дополнительный вопрос

Название функции может быть улучшено. Для меня for_each_pair/tuple звучит так, как будто должны быть возвращены все подмножества размера n диапазона (что само по себе является другой проблемой, которую я хотел бы решить). Поэтому я хотел бы получить несколько предложений о лучших именах, таких как:

for_each_adjacent<n>

Временное решение

Я разместил свое собственное решение в ЧР. Я не буду дублировать его здесь, потому что речь идет о стандартном решении, и уже есть достаточно ответов.

4b9b3361

Ответ 1

Простейшим было бы написать его как общий алгоритм, а затем применить его много раз.

 template< typename FwdIter, typename Func >
 Func for_each_pair( FwdIter iterStart, FwdIter iterEnd, Func func )
 {
     if( iterStart == iterEnd )
        return func;

     FwdIter iterNext = iterStart;
     ++iterNext;

     for( ; iterNext != iterEnd; ++iterStart, ++iterNext )
     {
          func( *iterStart, *iterNext );
     }
     return func;
}

Как меня спрашивали, почему он возвращает func (а не void), это типично для for_each из-за того, что

  • func может быть объектом
  • Он передается по значению.

func может "накапливать" какое-то состояние, но это копия, которую мы сделали в этот алгоритм, который накапливает его, а не пользовательский исходный объект. Поэтому мы передаем им измененный объект "func".

Ответ 2

Вы можете на самом деле злоупотреблять std::unique или std::adjacent_find для этого: предикат вызывается с каждой последовательной парой в диапазоне итераторов, и до тех пор, пока предикат всегда возвращает false, он ничего не изменит и не вернет раньше.

Не считая этого конкретного взлома, я бы реализовал это как адаптер итератора, а не алгоритм. То есть я бы выполнил consecutive_tuple_iterator<N>, который вернул бы все кортежи из N последовательных элементов. Тогда вы можете использовать его для таких вещей, как count_if и includes, а не только for_each. (Однако это не подходит для большинства алгоритмов модификации.)

Ответ 3

С С++ 11 и новыми вспомогательными функциями итератора std:: next и std:: prev для итераторов, второй вариант стандартного алгоритма std:: transform можно использовать для итерации по соседним элементам.

Вот пример, который генерирует список смежных пар из списка:

std::vector<int> nums{3, 4, 2, 9, 15, 267};
std::vector<std::pair<int,int>> num_pairs;
if (!nums.empty()) {
    std::transform(
        std::begin(nums), std::prev(std::end(nums)),
        std::next(std::begin(nums)),
        std::back_inserter(num_pairs),
        std::make_pair<
            decltype(nums)::const_reference,
            decltype(nums)::const_reference
        >
    );
}

Ответ 4

Не совсем то, что вы хотите, но посмотрите cpplinq.

int numbers[] = {0, 1, 2, 3, 4, 5};
auto pairs = cpplinq::from_array(numbers) 
          >> cpplinq::pairwise() 
          >> cpplinq::to_vector(); // yields (0,1), (1,2), (2,3), (3,4), (4,5)

for(auto p : pairs)
    f(p.first, p.second);