2010-04-14 2 views
2

Как сделать следующее более стильным/коротким способом?Как определить первый или последний элемент, итерирующий контейнер?

for(i=container.begin(); i!=container.end(); ++i) { 
    if (i!=container.begin()) { 
     cout << ", "; 
    } 
    cout << *i; 
    j=i; 
    if (++j==container.end()) { 
     cout << "!" << endl; 
    } 
} 

Такие решения, как foreach являются приемлемыми (действия по первому и последние элементы должны быть настраиваемыми, хотя).

P.S. Существует много ответов, которые обрабатывают первый элемент, но не последний. Вот что я имею в виду обработка последнего элемента:

for(i=container.begin(); i!=container.end(); ++i) { 
    j=i; 
    if (i==container.begin()) { 
     cout << "[" << *i << "]" << endl; 
    } else if (++j==container.end()) { 
     cout << ", (" << *i << ")" << "!" << endl; 
    } else { 
     cout << ", " << *i; 
    } 
} 

Не кажется ли вам, что это очень легко обрабатывать первый элемент вне тела цикла? Реальная проблема - последняя! Мне жаль, что я не смог прояснить важный вопрос, задающий вопрос. Я думаю, что в конце концов я получу окончательный ответ.

+2

Я не думаю, что ваш пример верен. container.end() - это элемент после последнего.В конце вашего forloop (для последнего элемента) он увеличивается и становится container.end(), который является вашим конечным условием. Правильно: 'i + 1 == container.end()'. – Thirler

+1

Поддерживается ли операция 'iterator + int 'для итераторов без случайного доступа? –

+0

Thirler вы прокомментировали вариант ++ j или предыдущий? Я внес некоторые изменения после сообщения об ошибке Neil. – Basilevs

ответ

3

Boost имеет next/prior, который иногда может помочь в таких ситуациях.

for(i=container.begin(); i!=container.end(); ++i) { 
    if (boost::next(i) == container.end()) { 
     std::cout << "!" << std::endl; 
    } 
} 

Хотя для этого конкретного случая, я бы просто вывести первый элемент, цикл от второго до последнего в то время как всегда выводя «» и затем выводит „!“ после окончания цикла. (Как и другие предложили уже)

Я не вижу смысла в перемещении особых случаев внутри цикла, а затем проверить внутри цикла для них ....

+0

+1 для boost :: следующее образование – paxos1977

+0

Это лучший ответ с моей точки зрения. См. Обновленный вопрос с разъяснениями. – Basilevs

2

В коде

if (i==container.end()) { 
    cout << "!" << endl; 
} 

никогда не произойдет.

Мой собственный подход заключается в использовании размера контейнера (я думаю, что size() теперь является постоянным временем для всех контейнеров стандартной библиотеки). Ведите счет в цикле, и вы находитесь в конце, когда count == size() - 1, и в начале, когда count == 0, очевидно.

+0

Исправлена ​​ошибка, о которой идет речь. Спасибо. – Basilevs

+0

Я думал, что в стандарте гарантируется только O (n) размер для контейнеров без случайного доступа? я бы, вероятно, пошел бы на это, если профилирование не показало, что это была проблема. –

+0

@jk. Было обсуждение об этом здесь некоторое время назад (что, конечно, я не могу найти) - единственный контейнер, который исторически не был O (1) для размера std :: list, но, похоже, TC или что-то изменило это - извините, но не более полезно, возможно, кто-то еще может подтвердить или опровергнуть это авторитетно? – 2010-04-14 11:48:32

1

СМЕЩЕНИЮ ++i немного:

i = container.begin(); 
while(i != container.end()) { 
    if (i != container.begin()) { 
     cout << ", "; 
    } 
    cout << *i; 
    if (++i == container.end()) { 
     cout << "!" << endl; 
    } 
} 
+0

Ницца. Удаление условия цикла и вставка разрыва в последнем блоке оператора if будет хорошим. – Basilevs

3

Мой совет здесь будет: нет никакого смысла в обнаружении ничего в этом цикле!

Поскольку ваши особые случаи находятся в начале и в конце вашего контейнера, их можно легко удалить изнутри цикла.

Следующая функция выведет содержимое любого класса контейнера, элементы которого могут быть << «эд к std::ostream:

template < class Container > 
void print(Container const & container) 
{ 
    typename Container::const_iterator current = container.begin(); 
    typename Container::const_iterator const end = container.end(); 
    if (current != end) 
    { 
     std::cout << *current; 
     for (++current; current != end; ++current) 
     { 
      std::cout << ", " << *current; 
     } 
     std::cout << "!" << std::endl; 
    } 
} 
+0

Для цикла лучше, чем во время. Хотя обработка последнего элемента отсутствует. – Basilevs

+0

Нет, последний элемент обработан правильно. end() возвращает итератор, который никогда не будет разыменован, он уже закончился. Если вы не собираетесь печатать '!' ? – bltxd

+0

Мой пример не демонстрирует этого, но вопрос явно запросил специальную обработку последнего элемента. Я не имею в виду * (container.end()) здесь. Я имею в виду последний действительный элемент. – Basilevs

1
template < class TContainerType> 
void print(TContainerType const & i_container) 
    { 
    typename TContainerTypeconst ::const_iterator current = i_container.begin(); 
    typename TContainerTypeconst ::const_iterator const end = i_container.end(); 
    if(current != end) 
    { 
    std::cout << *current++; 
    while(current != end) 
     std::cout << ", " << *current++; 
    } 
    std::cout << "!" << std::endl; 
    } 
2

Как container не определен вами, я использовал самый простой - vector

template <class T> 
string vector_join(const vector<T>& v, const string& token){ 
    ostringstream result; 
    for (typename vector<T>::const_iterator i = v.begin(); i != v.end(); i++){ 
    if (i != v.begin()) result << token; 
    result << *i; 
    } 
    return result.str(); 
} 

//usage 
cout << vector_join(container, ", ") << "!"; 
0

Возьмите вторую часть из ряда петля.

for(i=container.begin(); i!=container.end(); ++i) { 
    if (i != container.begin()) { 
     cout << ", "; 
    } 
    cout << *i; 
} 
cout << "!" << endl; 
+0

Как использовать последний элемент? i после точки цикла до конца(), которая не указывает на последний элемент. – Basilevs

Смежные вопросы