Russian Qt Forum

Программирование => С/C++ => Тема начата: kambala от Февраль 06, 2016, 16:59



Название: выбор направления прохода по контейнеру
Отправлено: kambala от Февраль 06, 2016, 16:59
Здравствуйте. Казалось бы, элементарная задача — бегать по контейнеру вперед или назад в зависимости от флага. Ну я сразу и написал (код упрощен):
Код
C++ (Qt)
void traverse(vector<int> v, bool reverse)
{
   auto it  = reverse ? v.crbegin() : v.cbegin();
   auto end = reverse ? v.crend()   : v.cend();
   while (it != end)
   {
       auto n = *it++;
       ...
   }
}
тут-то меня компилятор и обругал за разные типы прямого и обратного итераторов :(

сделал пока через std::reverse, но можно ли лучше?
Код
C++ (Qt)
void traverse(vector<int> v, bool reverse)
{
   auto vv = v;
   if (reverse)
       std::reverse(vv.cbegin(), vv.cend());
   for (auto n : vv)
   {
       ...
   }
}


Название: Re: выбор направления прохода по контейнеру
Отправлено: Igors от Февраль 06, 2016, 18:01
Понимаю что "код упрощен", но все же подача по значению, а потом еще одно копирование просто ужасно.
Я бы писал так
Код
C++ (Qt)
for (size_t i = 0; i < vec.size(); ++i) {
size_t index = reverse ? (vec.size() - i - 1) : i;
vec[index] = ...
}
 
А если нет доступа по индексу то так
Код
C++ (Qt)
auto itF = vec.begin();
auto itB = vec.rbegin();
 
whle (true) {
 if (!reverse && itF == vec.end()) break;
 if (reverse && itB == vec.rend()) break;
 auto & val = reverse ? (*itB++) : (*itF++);
 ...
}
 


Название: Re: выбор направления прохода по контейнеру
Отправлено: kambala от Февраль 06, 2016, 20:15
второй вариант как-то совсем не нравится.

по индексам норм. изначально не хотел с ними связываться т.к. они больше ни для чего не понадобятся.

спасибо.
но все же подача по значению, а потом еще одно копирование просто ужасно.
ну так это и есть упрощение :)


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 06, 2016, 21:01
Код
C++ (Qt)
template<typename Container, typename Func>
void func( Container c, Func func, bool reverse )
{
if( reverse )
for_each( c.crbegin(), c.crend(), func );
else
for_each( c.cbegin(), c.cend(), func );
}
 
int main( int, char *[] )
{
vector<int> data { 10, 20, 30, 40, 50 };
 
func( data, [=]( int v ){ cerr << v << endl; }, false );
 
return 0;
}
 


Название: Re: выбор направления прохода по контейнеру
Отправлено: Racheengel от Февраль 06, 2016, 22:59
Вот последний вариант, имхо, самый правильный.
Если бы еще не этот мерзкий синтаксис новых сей...


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 06, 2016, 23:03
Если бы еще не этот мерзкий синтаксис новых сей...
Вы про лябды? Так их не обяхательно использовать, можно обойтись функтором или обычной функцией.


Название: Re: выбор направления прохода по контейнеру
Отправлено: kambala от Февраль 07, 2016, 00:50
спасибо, про for_each не подумал. но мне внутри надо вызывать другую лямбду, у которой параметром не только элемент контейнера, так что не очень подходит.

остановился на использовании индексов.


Название: Re: выбор направления прохода по контейнеру
Отправлено: Racheengel от Февраль 07, 2016, 01:35
Если бы еще не этот мерзкий синтаксис новых сей...
Вы про лябды? Так их не обяхательно использовать, можно обойтись функтором или обычной функцией.

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


Название: Re: выбор направления прохода по контейнеру
Отправлено: Igors от Февраль 07, 2016, 06:07
Ну хоть auto добавили, и на этом спасибо :)
То палка о двух концах
Код
C++ (Qt)
   auto vv = v;
Не вижу типа - и это часто невыгодно/неудобно.

второй вариант как-то совсем не нравится.
Конечно он не идеален, но по меньшей мере показывает что можно обойтись парой-тройкой строк, и нырять в пучины какого-нибудь "value_type" совсем необязательно  :)


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 07, 2016, 07:49
но мне внутри надо вызывать другую лямбду, у которой параметром не только элемент контейнера
Не вижу проблемы. Внутренней лямбде могут быть доступны все данные.


Название: Re: выбор направления прохода по контейнеру
Отправлено: Igors от Февраль 07, 2016, 08:00
Вот последний вариант, имхо, самый правильный.
Если бы еще не этот мерзкий синтаксис новых сей...
Ну да, "к прогрессу (или моде) нужно быть поближе".

Хорошо, допустим такой код работает. Но вот набежали новые фичи/изменения, и в теле цикла понадобился break. Измените пожалуйста  :)


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 07, 2016, 08:12
Хорошо, допустим такой код работает. Но вот набежали новые фичи/изменения, и в теле цикла понадобился break. Измените пожалуйста  :)
Меняем for_each на  find_if, а из лямбды возвращаем true, когда нужно прервать цикл.


Название: Re: выбор направления прохода по контейнеру
Отправлено: kambala от Февраль 07, 2016, 13:43
Не вижу типа - и это часто невыгодно/неудобно.
мышкой на переменную навести :) хотя креатор типы контейнеров че-то не хочет показывать
но мне внутри надо вызывать другую лямбду, у которой параметром не только элемент контейнера
Не вижу проблемы. Внутренней лямбде могут быть доступны все данные.
да, это понятно. но данные эти придется тащить через еще один уровень :)


Название: Re: выбор направления прохода по контейнеру
Отправлено: Igors от Февраль 07, 2016, 13:52
да, это понятно. но данные эти придется тащить через еще один уровень :)
Ну и что? Зато как грамотно написано! И лямбды (причем вложенные, а не так себе), и темплейты и std - всем владеем виртуозно  :)


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 07, 2016, 14:50
Ну и что? Зато как грамотно написано! И лямбды (причем вложенные, а не так себе), и темплейты и std - всем владеем виртуозно  :)

Да, такое конечно получше: :)
Код
C++ (Qt)
auto itF = vec.begin();
auto itB = vec.rbegin();
 
whle (true) {
 if (!reverse && itF == vec.end()) break;
 if (reverse && itB == vec.rend()) break;
 auto & val = reverse ? (*itB++) : (*itF++);
 ...
}
 


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 07, 2016, 15:57
да, это понятно. но данные эти придется тащить через еще один уровень :)
Через какой уровень? В лямбде доступны все данные из среды ее описания, т.е. той функции в которой она определена, не зависимо от вложенности.

Вы бы показали больше кода, какие нужны данные и где вызывается вложенная лямбда.


Название: Re: выбор направления прохода по контейнеру
Отправлено: kambala от Февраль 07, 2016, 18:14
захватывать нужно ж необходимые переменные (предпочитаю не писать [=], если там не тонну захватить надо). не вижу смысла лишний раз это делать, если можно написать код без ущерба читабельности и понятливости.


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 07, 2016, 18:17
захватывать нужно ж необходимые переменные (предпочитаю не писать [=], если там не тонну захватить надо). не вижу смысла лишний раз это делать, если можно написать код без ущерба читабельности и понятливости.
Если так, то нет вопросов.


Название: Re: выбор направления прохода по контейнеру
Отправлено: Akon от Февраль 09, 2016, 12:37
kambala: имхо, ваш самый первый вариант самый очевидный/практичный. А чтобы компилятор не ругался на типы итераторов, параметризуйте:
Код:
template<typename Iterator>
void func(Iterator begin, const Iterator end)
{
    for (; end != begin; ++begin)
      // do something with *begin
}

std::vector<int> v;
func(v.begin, v.end);
func(v.rbegin, v.rend);


Название: Re: выбор направления прохода по контейнеру
Отправлено: Igors от Февраль 09, 2016, 13:00
А чтобы компилятор не ругался на типы итераторов, параметризуйте:
Код:
template<typename Iterator>
void func(Iterator begin, const Iterator end)
{
    for (; end != begin; ++begin)
      // do something with *begin
}

std::vector<int> v;
func(v.begin, v.end);
func(v.rbegin, v.rend);
Так теряется контекст вызывающего.


Название: Re: выбор направления прохода по контейнеру
Отправлено: Akon от Февраль 09, 2016, 20:45
Контекст параметром передать.


Название: Re: выбор направления прохода по контейнеру
Отправлено: Old от Февраль 09, 2016, 21:03
kambala: имхо, ваш самый первый вариант самый очевидный/практичный. А чтобы компилятор не ругался на типы итераторов, параметризуйте:
Вы только что изобрели for_each. :)


Название: Re: выбор направления прохода по контейнеру
Отправлено: Igors от Февраль 10, 2016, 13:08
Контекст параметром передать.
А не надорветесь?  :)

Тут поинтересовался немного как же сделать "по всем правилам", т.е. чтобы был полноценный итератор отвечающий всем требованиям std. Выходит как-то безумно, заоблачно сложно. В общем, "ни асилил"  :)


Название: Re: выбор направления прохода по контейнеру
Отправлено: Akon от Февраль 11, 2016, 11:10
Цитировать
Вы только что изобрели for_each.
Да, это работает как for_each. Я отталкивался от изначальной авторской реализации. for_each vs открытый итератор - это скорее дело вкуса.

Цитировать
А не надорветесь?
Ну если контекст не собран в какую-нибудь структуру/класс (функтор, если с for_each), то надо собрать.