2011-12-16 1 views
9

У меня есть std :: vector, скажем, целые числа для простоты.итерация контейнера STL не из .begin() ing и wrap вокруг

std::vector<int> ivec; 
ivec.push_back(1); 
ivec.push_back(2); 
... //omitting some push back's 3 to 99 
ivec.push_back(100); 

Стандартный способ итерации известно

std::map<int>::iterator it; 
for(it = ivec.begin(); it != ivec.end(); it++) 
    print(); 

Это итерация напечатает 1,2,3, ... 100.

Я хочу, чтобы пройти все векторные элементы, начиная с предопределенный индекс, а не из него. begin(). Я хотел бы напечатать

3,4,5,6 ... 99, 100, 1, 2

Можете ли вы поделиться своими мыслями здесь?

Это может нормально сделать это в два этапа

for(it = ivec.begin()+index; it != ivec.end(); it++) and then (if index !=0) 

for (it = ivec.begin; it = it = ivec.begin() + (index-1); it++) 
+1

Вы можете написать оболочку итератора, которая отображает желаемое поведение. Но если вам это нужно больше, чем пару раз, это, вероятно, слишком велико. – fredoverflow

+0

Ознакомиться с буфером кругового буфера: http://www.boost.org/doc/libs/1_48_0/libs/circular_buffer/doc/circular_buffer.html – fouronnes

+0

Ваш последний пример неправильный. Во втором цикле условие должно быть 'it! = Ivec.begin() + index' (конечно, это работает только для случайных итераторов), и вам не нужно, чтобы' if index! = 0', условие цикла делает это для вас. –

ответ

3
bool wrapped = false; 
for (auto it = vec.begin() + index; (it != vec.begin() + index) || !wrapped; ++it) 
{ 
    if (it == vec.end()) 
    { 
     it = vec.begin(); 
     wrapped = true; 
    } 
    std::cout << *it; 
} 
+0

Разве это '' 'вместо' &&'? –

+0

@ChristianRau Что бы вышло из цикла в самом начале, нет? – jrok

+0

Нет, так как в начале '! Wrapped' является' true'. После обертывания '! Wrapped' является' false', а OR эквивалентно только условию итератора. В вашем случае оно заканчивается в начале, так как условие итератора является ложным и, следовательно, AND. –

7

Вы можете:

  • разработать класс итератора, который оборачивает вектор :: итератор и разоблачить поведение вам нравится (в в частности: ++ проверяет на end() и заменяет его на begin() и корректирует другие «значения границ»)

  • заполняет вектор, начиная с 3 и обертывая 100, тотерация будет выглядеть так, как вы хотите.

Выбор зависит от того, что еще представляет собой вектор и что еще нужно для этой итерации.

+0

-1 Для сложного решения очень простой задачи. –

+7

@PaulManta: если проблема «локализованная», это тривиально. Если проблема «обобщена», если она не является тривиальной: если вы хотите, чтобы ваша итерация работала с любым существующим алгоритмом STL ... вы должны определить итератор. Я думаю, что ваше голосование основано на предрассудке. –

0

Есть бесконечные способы сделать это, и, возможно, все они (более или менее) эквивалентны, поэтому в конечном итоге это зависит от личных предпочтений и, возможно, от правил стиля кодирования. Я бы, наверное, это нравится:

std::cout << v[idx] << "\n"; 
for(auto it = v.begin() + idx + 1; it != v.begin()+idx; ++it) 
{ 
    if(it == v.end()) it = v.begin(); 
    std::cout << *it << "\n"; 
} 
+0

Почему вы используете авто? – cateof

+1

@cateo: Я всегда использую его для хранения итераторов, так как он делает код менее запутанным и во многих случаях позволяет мне писать общий код проще, поскольку мне не нужно обращаться к вложенным typedefs. – PlasmaHH

+0

OP, вероятно, не знает об этом, так как это C++ 11. – jrok

2

Раствор при использовании контейнера случайного доступа очень просто, смотрите ниже код.

std::vector<int> v ({1,3,4,5,6,7,8,9,10}); /* c++11 */ 

... 

for (int i = 2; i < (10+2); ++i) 
    std::cout << v[i % 10] << " "; 

Метод при использовании контейнеров только имеющих двунаправленную/вперед итераторы:

std::list<int> l ({1,3,4,5,6,7,8,9,10}); /* c++11 */ 

    Iter start = l.begin(); 
    std::advance (start, 4); 

    ... 

    Iter it = start; 

    do { 
    std::cerr << *it << std::endl; 

    } while (
    (it = ++it == l.end() ? l.begin() : it) != start 
); 
+0

+1 Для приятного состояния. –

4

Я предполагаю, что у вас уже есть начальный итератор. Как вы это понимаете, зависит от того, используете ли вы индексный (векторный) тип или только прямой итератор или тип с ключом. Затем вы можете сделать цикл примерно так:

type::iterator start_iter = /* something from collection, perhaps begin()+index */ 
type::iterator cur_iter = start_iter; 
do 
{ 
    //do something with cur_iter 

    ++cur_iter; 
    if(cur_iter == collection.end()) 
    cur_iter = collection.begin(); 
} while(cur_iter != start_iter); 

Это основной цикл.

+0

почему '.start();'? – PlasmaHH

+0

Извините, должен был быть '.begin()' –

0

Я знаю, что это довольно старый вопрос, но никто не упомянул std::rotate, который, я думаю, в некоторых случаях может быть правильным инструментом для работы.

Модифицированный пример из http://www.cplusplus.com/reference/algorithm/rotate/:

#include <iostream>  // std::cout 
#include <algorithm> // std::rotate 
#include <vector>  // std::vector 

int main() { 
    std::vector<int> myvector; 

    // set some values: 
    for (int i=1; i<10; ++i) myvector.push_back(i); // 1, 2, 3, ... 9 

    std::rotate(myvector.begin(),myvector.begin()+2,myvector.end()); 
                // 3, 4, 5, 6 ... 9, 1, 2 
    // print out content: 
    std::cout << "myvector contains:"; 
    for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it) 
    std::cout << ' ' << *it; 
    std::cout << '\n'; 

    return 0; 
} 

Выход:

myvector contains: 3 4 5 6 7 8 9 1 2 

Обратите внимание, поскольку my_vector модифицируется функцией std::rotate, это не является ни очень эффективным, ни полезно, если вы просто хотите итерируем вектор один раз.

Однако, хотя это, вероятно, не лучший ответ на этот вопрос SO, я надеюсь, что он все равно может дать некоторую ценность людям с подобными проблемами.

+0

Ваш код изменяет оригинальный вектор. В некоторых случаях это может быть не идеальным. – JohnKoch

+0

Это хороший момент, я обновляю свой ответ, чтобы сделать его более понятным. – jpihl