Получаем смежные пары элементов из vector с помощью итератора в c

#c

#c

Вопрос:

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

 (1, 2)
(2, 3)
(3, 4)
  

Я знаю, как выполнять итерацию по одному элементу за раз, используя следующее:

 vector<int> numbers == {1, 2, 3, 4};
for (vector<int>::const_iterator it = numbers.cbegin(); 
     words != numbers.cend(); 
       it) 
{
    cout << *it << endl;
}
  

Но я не знаю, как также получить следующий элемент.

Комментарии:

1. Получаем следующий элемент с помощью std::next ! Однако вам нужно, чтобы ваш цикл останавливал один элемент раньше.

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

3. Или просто *(it 1) , но std::next является более общим

Ответ №1:

Итератор Vector — это итератор произвольного доступа. Вы можете использовать operator[] в итераторе, чтобы получить следующий элемент, подобный этому: it[1]

Комментарии:

1. @Peter Но это очевидно, не так ли?

2. Одна вещь, которую я узнал на этом сайте, заключается в том, что когда кто-то оставляет что-то недосказанным, потому что считает это «очевидным», это почти наверняка не очевидно для кого-то другого.

Ответ №2:

std::vector::iterator может использоваться почти как указатель.

Убедитесь, что условие цикла изменено, и используйте *(it 1) в цикле.

 vector<int> numbers = {1, 2, 3, 4}; // One =, not two.

// If the vector is empty, skip the block.
if ( !numbers.empty() )
{
   vector<int>::const_iterator end = numbers.cend() - 1;

   for (vector<int>::const_iterator it = numbers.cbegin(); it != end;   it) {
      cout << '(' << *it << ',' << *(it 1) << ')' << endl;
   }
}
  

Рабочая демонстрация.

Комментарии:

1. @6502 это не проблема. Смотрите end = numbers.cend() - 1

2. @6502, этого не произойдет, потому что условие гарантирует это.

3. Это отличный ответ! Зачем разыменовывать итератор вместо использования it[1] , как в ответе @wilx’s?

4. @goatboy3million, хотя в стандарте указано, что это it[1] переводится как *(it 1) , я предпочитаю использовать первое только для массивов.

5. Это приводит к неопределенному поведению, если it перед концом стоит единица (или если вектор пуст).

Ответ №3:

 for (auto it = numbers.cbegin(); it != numbers.end() amp;amp; std::next(it) != numbers.end();   it)
{
    cout << *it << " " << *std::next(it) << endl;

    // or, abusing C  17
    autoamp;amp; [first, second] = std::tie(*it, *std::next(it));
    cout << first << " " << second << endl;
}
  

Если вам действительно нужен итератор, который выдаст пару, я наскоро собрал один для подтверждения концепции. Остерегайтесь, это не тщательно протестировано.

Использование:

 for (autoamp;amp; [first, second] : AdjacentRange{numbers})
{
    cout << first << " " << second << endl;
}

// or

for (autoamp;amp; pair : AdjacentRange{numbers})
{
    cout << pair.first << " " << pair.second << endl;
}
  

Реализация:

 template <class It>
struct AdjancetRangeIt
{
    It it_;

    auto operator*() const
    {
        return std::tie(*it_, *std::next(it_));
    }

    auto operator  () -> autoamp; {   it_; return *this; }
    auto operator!=(AdjancetRangeIt other) const { return it_ != other.it_; }
};

template <class It>
AdjancetRangeIt(It) -> AdjancetRangeIt<It>;

template <class It>
struct AdjacentRange
{
    It begin_, end_;

    template <class C>
    AdjacentRange(Camp; container)
        : begin_{std::begin(container)}, end_{std::end(container)}
    {}

    auto begin() const
    {
        return AdjancetRangeIt{begin_};
    }
    auto end() const
    {
        if (begin_ == end_)
            return AdjancetRangeIt{end_};
        else
            return AdjancetRangeIt{std::prev(end_)};
    }
};

template <class C>
AdjacentRange(C) -> AdjacentRange<typename C::iterator>;
  

Ответ №4:

Вы можете выполнять итерации по двум элементам одновременно:

 std::vector<int> v{1,2,3,4};

for (auto l = v.begin(), r = l   1, e = v.end(); r != e;   l,   r)
    std::cout << '(' << *l << ", " << *r << ")n";
  

Выводит:

 (1, 2)
(2, 3)
(3, 4)
  

Проверьте это в Wandbox.

Комментарии:

1. Что происходит с пустым контейнером?

2. @6502 r != e вычисляется как false и, таким образом, мы не входим в цикл .

3. Нет… в пустом контейнере l равно begin() , которое также равно e ; r вместо этого вычисляется как l 1 , т.е. begin() 1 т.е. end() 1 … таким образом…

4.@6502 вы правы, r != e не вычисляет false , ни вводит идентификатор цикла, почему. В любом случае, исправление довольно простое: как и в других ответах, просто проверьте пустоту.

Ответ №5:

Если вам нужно работать с std::vector это тот случай, когда использование итераторов бессмысленно … простым способом использования индексов было бы

 for (int i=0,n=numbers.size(); i<n-1; i  ) {
    std::cout << '(' << numbers[i] << ", " << numbers[i 1] << ")n";
}
  

Конечно, это можно сделать с помощью итераторов, но это будет отстой.

Ответ №6:

В предстоящем выпуске C 23 вы сможете использовать views::slide(2) и views::смежный(2). slide() создает диапазон диапазонов, тогда как смежный() создает диапазон кортежей. Пример:

 vector<int> ints = {1, 2, 3, 4};
auto rg = ints | views::adjacent(2);
/*{{1, 2}, {2, 3}, {3, 4}}*/

auto [value1, value2] = (ints | views::adjacent(2))[0];
//value1=1
//value2=2