Чи можна повторити вектор з кінця до початку?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Або це можливо лише з подібним:
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Чи можна повторити вектор з кінця до початку?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Або це можливо лише з подібним:
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Відповіді:
Найкращий спосіб:
for (vector<my_class>::reverse_iterator i = my_vector.rbegin();
i != my_vector.rend(); ++i ) {
}
rbegin()
/ rend()
були спеціально розроблені для цієї мети. (І так, при збільшенні a reverse_interator
рухається назад).
Тепер теоретично ваш метод (з використанням begin()
/ end()
& --i
) буде працювати, std::vector
ітератор буде двонаправленим, але пам’ятайте, end()
це не останній елемент - це один за останнім елементом, тому вам доведеться декрементувати спочатку, і ви робиться, коли ви досягаєте begin()
- але вам все одно доведеться виконати обробку.
vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
--i;
/*do stuff */
}
ОНОВЛЕННЯ: Я, мабуть, був занадто агресивним, переписуючи for()
цикл у while()
цикл. (Важливим є те, що --i
це на початку.)
--i
це спричинить велику проблему, якщо контейнер порожній ... Перед входом у do - while
цикл має сенс перевірити (my_vector.begin() != my_vector.end())
.
do-while
цикл замість просто while
циклу? Тоді вам не знадобиться спеціальна перевірка порожніх векторів.
auto
для кращої читабельності?
Добре встановлений "шаблон" для зворотної ітерації через закрито-відкриті діапазони виглядає наступним чином
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator-- != begin; ) {
// Process `*iterator`
}
або, якщо вам більше подобається,
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator != begin; ) {
--iterator;
// Process `*iterator`
}
Цей шаблон корисний, наприклад, для зворотної індексації масиву з використанням беззнакового індексу
int array[N];
...
// Iterate over [0, N) range in reverse
for (unsigned i = N; i-- != 0; ) {
array[i]; // <- process it
}
(Люди, незнайомі з цим шаблоном, часто наполягають на використанні цілочисельних типів із підписом для індексації масивів, оскільки вони неправильно вважають, що типи без знаків якимось чином "непридатні" для зворотного індексування)
Його можна використовувати для ітерації масиву за допомогою техніки "ковзання вказівника"
// Iterate over [array, array + N) range in reverse
for (int *p = array + N; p-- != array; ) {
*p; // <- process it
}
або його можна використовувати для зворотної ітерації по вектору за допомогою звичайного (не зворотного) ітератора
for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) {
*i; // <- process it
}
--end()
end()
. Хоча вони, здається, починаються з end()
, вони завжди переконуються зменшити ітератор перед першим доступом.
auto a = vector<int>{0,1,2}; bool reversed = 0; auto it = (!reversed?a.begin():a.end()); auto end = (reversed?a.begin():a.end());
while(it != end) { if(reversed)--it; cout << *it << endl; if(!reversed)++it; }
reversed
чотири рази - два з них у циклі. Звичайно, тестування логічної будови відбувається дуже швидко, але все ж, чому працювати не потрібно? Особливо, оскільки, здається, єдиною метою є зробити код нечитабельним. як щодо того, що ми використовуємо дві окремі петлі? if (reversed) for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) {doStuff(*it);} else for (auto it = my_vector.begin(); it != my_vector.end(); ++it) {doStuff(*it);}
if
s, але я хотів позбутися шаблону на doStuff()
. Все ще здійсненно, хоча з двома, if
які ви маєте, прокручуючи навпаки на першому.
Починаючи з c ++ 20, ви можете використовувати std::ranges::reverse_view
цикл for і на основі діапазону:
#include<ranges>
#include<vector>
#include<iostream>
using namespace std::ranges;
std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for(auto& i : views::reverse(vec)) {
std::cout << i << ",";
}
Або навіть
for(auto& i : vec | views::reverse)
На жаль, на момент написання статті (січень 2020 р.) Жоден основний компілятор не реалізує бібліотеку діапазонів, але ти можеш вдатися до діапазону Еріка Ніблер-v3 :
#include <iostream>
#include <vector>
#include "range/v3/all.hpp"
int main() {
using namespace ranges;
std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for(auto& i : views::reverse(vec)) {
std::cout << i << ",";
}
return 0;
}
template<class It>
std::reverse_iterator<It> reversed( It it ) {
return std::reverse_iterator<It>(std::forward<It>(it));
}
Тоді:
for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) {
std::cout << *rit;
В якості альтернативи в C ++ 14 просто виконайте:
for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) {
std::cout << *rit;
У C ++ 03/11 більшість стандартних контейнерів мають .rbegin()
і .rend()
метод , а також.
Нарешті, ви можете написати адаптер діапазону backwards
наступним чином:
namespace adl_aux {
using std::begin; using std::end;
template<class C>
decltype( begin( std::declval<C>() ) ) adl_begin( C&& c ) {
return begin(std::forward<C>(c));
}
template<class C>
decltype( end( std::declval<C>() ) ) adl_end( C&& c ) {
return end(std::forward<C>(c));
}
}
template<class It>
struct simple_range {
It b_, e_;
simple_range():b_(),e_(){}
It begin() const { return b_; }
It end() const { return e_; }
simple_range( It b, It e ):b_(b), e_(e) {}
template<class OtherRange>
simple_range( OtherRange&& o ):
simple_range(adl_aux::adl_begin(o), adl_aux::adl_end(o))
{}
// explicit defaults:
simple_range( simple_range const& o ) = default;
simple_range( simple_range && o ) = default;
simple_range& operator=( simple_range const& o ) = default;
simple_range& operator=( simple_range && o ) = default;
};
template<class C>
simple_range< decltype( reversed( adl_aux::adl_begin( std::declval<C&>() ) ) ) >
backwards( C&& c ) {
return { reversed( adl_aux::adl_end(c) ), reversed( adl_aux::adl_begin(c) ) };
}
і тепер ви можете зробити це:
for (auto&& x : backwards(ctnr))
std::cout << x;
що, на мою думку, досить гарне.
Використовуйте зворотні ітератори та цикл від rbegin()
доrend()
Ось надзвичайно проста реалізація, яка дозволяє використовувати для кожної конструкції і покладається лише на бібліотеку C ++ 14 std:
namespace Details {
// simple storage of a begin and end iterator
template<class T>
struct iterator_range
{
T beginning, ending;
iterator_range(T beginning, T ending) : beginning(beginning), ending(ending) {}
T begin() const { return beginning; }
T end() const { return ending; }
};
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// usage:
// for (auto e : backwards(collection))
template<class T>
auto backwards(T & collection)
{
using namespace std;
return Details::iterator_range(rbegin(collection), rend(collection));
}
Це працює з речами, які постачають rbegin () і rend (), а також зі статичними масивами.
std::vector<int> collection{ 5, 9, 15, 22 };
for (auto e : backwards(collection))
;
long values[] = { 3, 6, 9, 12 };
for (auto e : backwards(values))
;
Мені подобається зворотний ітератор в кінці Якка - відповідь Адама Неврамонта, але він здавався складним для того, що мені потрібно, тому я написав це:
template <class T>
class backwards {
T& _obj;
public:
backwards(T &obj) : _obj(obj) {}
auto begin() {return _obj.rbegin();}
auto end() {return _obj.rend();}
};
Я можу взяти звичайний ітератор, такий:
for (auto &elem : vec) {
// ... my useful code
}
і змініть його на це, щоб повторити в зворотному порядку:
for (auto &elem : backwards(vec)) {
// ... my useful code
}
Якщо ви можете використовувати бібліотеку Boost, існує Boost.Range, який забезпечує reverse
адаптер діапазону , включаючи:
#include <boost/range/adaptor/reversed.hpp>
Потім, у поєднанні з діапазоном for
циклу C ++ 11 , ви можете просто написати наступне:
for (auto& elem: boost::adaptors::reverse(my_vector)) {
// ...
}
Оскільки цей код коротший, ніж той, що використовує пару ітераторів, він може бути більш читабельним і менш схильним до помилок, оскільки на нього менше деталей, на які слід звертати увагу.
використовувати цей код
//print the vector element in reverse order by normal iterator.
cout <<"print the vector element in reverse order by normal iterator." <<endl;
vector<string>::iterator iter=vec.end();
--iter;
while (iter != vec.begin())
{
cout << *iter << " ";
--iter;
}
Оскільки я не хочу вводити новий синтаксис чужого Марса, і я просто хочу створити на основі існуючих примітивів, наведені нижче фрагменти працюють:
#include <vector>
#include <iostream>
int main (int argc,char *argv[])
{
std::vector<int> arr{1,2,3,4,5};
std::vector<int>::iterator it;
for (it = arr.begin(); it != arr.end(); it++) {
std::cout << *it << " ";
}
std::cout << "\n************\n";
for (it = arr.end() - 1; it != arr.begin()-1;it--) {
std::cout << *it << " ";
}
return 0;
}