Может я чего-то не понимаю....
От: B0FEE664  
Дата: 25.07.23 22:15
Оценка:
Знакомясь с возможностями C++20 наткнулся на статью здесь
Предлагается решить задачу:

Допустим, есть вектор с целыми числами, и требуется поделить все его числа на два. Те, которые не делятся, нужно просто выкинуть. Это несложно написать циклом, но для выразительности воспользуемся алгоритмами:

std:vector<int> numbers_in;
std:vector<int> numbers_out;

// задача: поделить на 2 все чётные числа из numbers_in
// и записать результаты numbers_out
std:vector<int> intermediate;

// скопируем в intermediate только чётные числа
std::copy_if(numbers_in.begin(), numbers_in.end(),  
             std::back_inserter(intermediate), 
             [](int x) {
                        return x % 2 == 0;
             }
);

// поделим их на 2
std::transform(intermediate.begin(), intermediate.end(),
               std::back_inserter(numbers_out),
               [](int x) {
                          return x / 2;
               }
)

Немного удивившись такому решению, быстрым поиском нашёл похожее у MS здесь
Почему так сложно? Ведь задача решается в одну строчку:
std::remove_if(numbers_in.begin(), numbers_in.end(), [](int& x){ const int s = x; x /= 2; return (s%2)==1; } );

Исправление (забыл отрезать хвост):
numbers_in.erase(std::remove_if(numbers_in.begin(), numbers_in.end(), [](int& x){ const int s = x; x /= 2; return (s%2)==1; } ), numbers_in.end());



Или так нельзя?
И каждый день — без права на ошибку...
Отредактировано 26.07.2023 10:03 B0FEE664 . Предыдущая версия . Еще …
Отредактировано 26.07.2023 10:01 B0FEE664 . Предыдущая версия .
Отредактировано 25.07.2023 22:15 B0FEE664 . Предыдущая версия .
Re: Может я чего-то не понимаю....
От: bnk СССР http://unmanagedvisio.com/
Дата: 25.07.23 22:23
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему так сложно? Ведь задача решается в одну строчку:

BFE>
BFE>std::remove_if(numbers_in.begin(), numbers_in.end(), [](int& x){ const int s = x; x /= 2; return (s%2)==1; } );
BFE>

BFE>Или так нельзя?

Можно, но придется дописать, т.к. remove_if не удаляет элементы, несмотря на свое название.
Re: Может я чего-то не понимаю....
От: Pzz Россия https://github.com/alexpevzner
Дата: 25.07.23 23:19
Оценка: +3 :)
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему так сложно? Ведь задача решается в одну строчку:


В одну строчку — это для любителей. А профессионал напишет полэкрана текста, как в приведенном тобой примере. Потому, что он профессионал.
Re: Может я чего-то не понимаю....
От: rg45 СССР  
Дата: 25.07.23 23:27
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Немного удивившись такому решению, быстрым поиском нашёл похожее у MS здесь

BFE>Почему так сложно? Ведь задача решается в одну строчку:
BFE>
BFE>std::remove_if(numbers_in.begin(), numbers_in.end(), [](int& x){ const int s = x; x /= 2; return (s%2)==1; } );
BFE>

BFE>Или так нельзя?

Ну, во-первых, правильно выше подсказывают, здесь не хватает вызова erase. И если его добавить, то твоя строчка становится чуть длиннее:

   numbers_in.erase(std::remove_if(numbers_in.begin(), numbers_in.end(), [](int& x){ const int s = x; x /= 2; return (s%2)==1; } ), numbers_in.end());


Во-вторых, по условию задачи результат нужно было сложить в другой массив, а ты удаляешь прямо в исходном. Или ты собираешься сперва скопировать все содержимое, а потом уже удалять? Это вообще не спортивно.

Ну и в-третьих, а чем, в данном случае, хуже "лобовое" решение с использованием обычного цикла?

  for(int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);


А, ну и по сути вопроса — "почему так сложно?" — а ХЗ, почему. Мозги у людей как-то странно сложены.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 26.07.2023 6:58 rg45 . Предыдущая версия . Еще …
Отредактировано 25.07.2023 23:46 rg45 . Предыдущая версия .
Отредактировано 25.07.2023 23:30 rg45 . Предыдущая версия .
Отредактировано 25.07.2023 23:29 rg45 . Предыдущая версия .
Re[2]: Может я чего-то не понимаю....
От: bnk СССР http://unmanagedvisio.com/
Дата: 26.07.23 00:00
Оценка: -1
Здравствуйте, rg45, Вы писали:

R>
numbers_in.erase(std::remove_if(numbers_in.begin(), numbers_in.end(), [](int& x){ const int s = x; x /= 2; return (s%2)==1; } ), numbers_in.end());


R>А, ну и по сути вопроса — "почему так сложно?" — а ХЗ, почему. Мозги у людей как-то странно сложены.


Особенно сравнивая с нормальными языками

numbers_in.filter(x => x % 2 == 0).map(x => x / 2)
Re[3]: Может я чего-то не понимаю....
От: rg45 СССР  
Дата: 26.07.23 06:18
Оценка: +2 :)
Здравствуйте, bnk, Вы писали:

R>
for(int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);


bnk>Особенно сравнивая с нормальными языками


bnk>
numbers_in.filter(x => x % 2 == 0).map(x => x / 2)


Ну ты же жульничаешь — ты не положил числа в выходной массив. Ты предоставть законченное решение, тогда и сравним.

И запись, с которой сравниваешь, удалил зачем-то. Наверное, для того, чтобы сделать сравнение максимально наглядным

А во-вторых, в С++ тоже можно решить эту задачу в функциональном стиле и с lazy evaluation. Необходимая поддержка для этого давно уже есть в boost и сравнительно недавно появилась в стандартной библиотеке, в std::ranges:

http://coliru.stacked-crooked.com/a/dda09192df73cb9d

numbers_in | filtered([](int x) { return !(x % 2); }) | transformed([](int x) { return x / 2; })


Ну да, в C++ лябмды выглядят не столь компактными как в "нормальных" языках, но мы же не состязаемся в синтаксическом кариесе, правда?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 26.07.2023 7:00 rg45 . Предыдущая версия . Еще …
Отредактировано 26.07.2023 6:59 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 6:57 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 6:56 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 6:32 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 6:29 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 6:22 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 6:21 rg45 . Предыдущая версия .
Re[4]: Может я чего-то не понимаю....
От: bnk СССР http://unmanagedvisio.com/
Дата: 26.07.23 07:16
Оценка:
Здравствуйте, rg45, Вы писали:

bnk>>
numbers_in.filter(x => x % 2 == 0).map(x => x / 2)


R>Ну ты же жульничаешь — ты не положил числа в выходной массив. Ты предоставть законченное решение, тогда и сравним.


R>И запись, с которой сравниваешь, удалил зачем-то. Наверное, для того, чтобы сделать сравнение максимально наглядным


Ну совсем немного и сжульничал

R>А во-вторых, в С++ тоже можно решить эту задачу в функциональном стиле и с lazy evaluation. Необходимая поддержка для этого давно уже есть в boost и сравнительно недавно появилась в стандартной библиотеке, в std::ranges:


R>
R>numbers_in | filtered([](int x) { return !(x % 2); }) | transformed([](int x) { return x / 2; })
R>


R>Ну да, в C++ лябмды выглядят не столь компактными как в "нормальных" языках, но мы же не состязаемся в синтаксическом кариесе, правда?


Ну, да, выглядит получше. Хотя "=>" вместо того ужаса, что есть была бы как-то понятнее IMHO.
Re[5]: Может я чего-то не понимаю....
От: rg45 СССР  
Дата: 26.07.23 07:21
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Ну, да, выглядит получше. Хотя "=>" вместо того ужаса, что есть была бы как-то понятнее IMHO.


Ну, прямо "ужас" — лямбды не на птичьем языке записываются. По-моему, данная тема и данный форум — не лучшее место для обсуждения таких вопросов.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Может я чего-то не понимаю....
От: so5team https://stiffstream.com
Дата: 26.07.23 08:12
Оценка:
Здравствуйте, rg45, Вы писали:

bnk>>
numbers_in.filter(x => x % 2 == 0).map(x => x / 2)


R>Ну ты же жульничаешь — ты не положил числа в выходной массив. Ты предоставть законченное решение, тогда и сравним.


Это, кстати, от языка зависит. В некоторых языках map именно что создает новый контейнер:
r = [3, 4, 5, 6, 7, 8].filter {|x| x % 2 == 0}.map {|x| x/2}
p r # => [2, 3, 4]


Другое дело, что в таких языках никого не парит что за контейнер будет создан, как он будет расти и т.д., и т.п.
Re[5]: Может я чего-то не понимаю....
От: rg45 СССР  
Дата: 26.07.23 08:33
Оценка:
Здравствуйте, so5team, Вы писали:

bnk>>>
numbers_in.filter(x => x % 2 == 0).map(x => x / 2)


R>>Ну ты же жульничаешь — ты не положил числа в выходной массив. Ты предоставть законченное решение, тогда и сравним.


S>Это, кстати, от языка зависит. В некоторых языках map именно что создает новый контейнер:



Ну, как минимум, нужно сделать присваивание (инициализацию) — в любом языке. А если это
Автор: bnk
Дата: 26.07.23
C# (пардон, если не угадал), то еще и добавить преобразование к нужному типу. И если это сделать, то совсем не понятно, ради чего нужно было заводить очередной холивар:

numbers_out = numbers_in.filter(x => x % 2 == 0).map(x => x / 2).ToArray<int>();


for(int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 26.07.2023 8:58 rg45 . Предыдущая версия . Еще …
Отредактировано 26.07.2023 8:58 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 8:58 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 8:56 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 8:46 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 8:38 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 8:38 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 8:37 rg45 . Предыдущая версия .
Re[4]: Может я чего-то не понимаю....
От: sergii.p  
Дата: 26.07.23 09:02
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну ты же жульничаешь — ты не положил числа в выходной массив.


это как акцент поставить. До С++20 нельзя сделать std::transform без создания выходного массива. В оригинальной статье явно этот момент и отмечался.
Там же есть уточнение:

Но если всё же вам необходимо вычислить всё сразу, вы можете по старинке сложить все элементы диапазона в какой-нибудь контейнер:

numbers_vector = std::vector(number_out.begin(), numbers_out.end());

Re[5]: Может я чего-то не понимаю....
От: rg45 СССР  
Дата: 26.07.23 09:11
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>это как акцент поставить. До С++20 нельзя сделать std::transform без создания выходного массива. В оригинальной статье явно этот момент и отмечался.


Так я проставил акцент: http://rsdn.org/forum/cpp/8568552.1
Автор: rg45
Дата: 26.07.23


Ну и в-третьих, а чем, в данном случае, хуже "лобовое" решение с использованием обычного цикла?


В данном случае выходной массив создается по условию задачи
Автор: B0FEE664
Дата: 26.07.23
.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Может я чего-то не понимаю....
От: ArtDenis Россия  
Дата: 26.07.23 09:31
Оценка: +3
Здравствуйте, rg45, Вы писали:

R>Ну и в-третьих, а чем, в данном случае, хуже "лобовое" решение с использованием обычного цикла?


R>
R>  for(int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);
R>


Для плюсов — идеальный вариант, т.к. другие или корявые или сложные
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[6]: Может я чего-то не понимаю....
От: sergii.p  
Дата: 26.07.23 09:37
Оценка:
Здравствуйте, rg45, Вы писали:

R>

R>Ну и в-третьих, а чем, в данном случае, хуже "лобовое" решение с использованием обычного цикла?


R>В данном случае выходной массив создается по условию задачи
Автор: B0FEE664
Дата: 26.07.23
.


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

Допустим, есть вектор с целыми числами, и требуется поделить все его числа на два. Те, которые не делятся, нужно просто выкинуть.


и там же дано объяснение почему "лобовое" решение "хуже"

Это несложно написать циклом, но для выразительности воспользуемся алгоритмами


"выразительность" — понятие условное конечно. Лично мне эти "фломастеры" нравятся.
Re[2]: Может я чего-то не понимаю....
От: B0FEE664  
Дата: 26.07.23 10:21
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>Во-вторых, по условию задачи результат нужно было сложить в другой массив, а ты удаляешь прямо в исходном. Или ты собираешься сперва скопировать все содержимое, а потом уже удалять? Это вообще не спортивно.

Нет, это в примере решена не та задача, которая поставлена.

R>Ну и в-третьих, а чем, в данном случае, хуже "лобовое" решение с использованием обычного цикла?

R>
R>  for(int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);
R>

Ну, если добавить const:
for(const int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);

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

R>А, ну и по сути вопроса — "почему так сложно?" — а ХЗ, почему. Мозги у людей как-то странно сложены.

Мне кажется, что подобную задачу в качестве примера я не первый раз встречаю. Все её решают почему-то через промежуточный буфер... Отсюда и вопрос.
И каждый день — без права на ошибку...
Re[7]: Может я чего-то не понимаю....
От: rg45 СССР  
Дата: 26.07.23 10:35
Оценка: -1
Здравствуйте, sergii.p, Вы писали:


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


Учу читать, пока бесплатно:

// задача: поделить на 2 все чётные числа из numbers_in
// и записать результаты в numbers_out
std:vector<int> intermediate;


здесь
Автор: B0FEE664
Дата: 26.07.23
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 26.07.2023 10:37 rg45 . Предыдущая версия . Еще …
Отредактировано 26.07.2023 10:37 rg45 . Предыдущая версия .
Отредактировано 26.07.2023 10:36 rg45 . Предыдущая версия .
Re[3]: Может я чего-то не понимаю....
От: rg45 СССР  
Дата: 26.07.23 10:43
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Нет, это в примере решена не та задача, которая поставлена.


Ну, так а я тут при чем? Я вижу в ТВОЕМ сообщении формулировку: "задача: поделить на 2 все чётные числа из numbers_in и записать результаты в numbers_out". Я же с тобой общаюсь, поэтому то, что написано тобой я рассматриваю с более высоким приоритетом чем то, что находится по ссылкам. А если в твоем сообщении присутствуют противоречащие друг другу формулировки, то это твоя забота рассказать, что читать, а что игнорировать. В чем я не прав?

BFE>Ну, если добавить const:

BFE>
BFE>for(const int x : numbers_in) if (!(x % 2)) numbers_out.push_back(x / 2);
BFE>

BFE>то нормально, если нам действительно нужно копирование.

И почему здесь так важен этот const, не объяснишь? Помимо правил хорошего стиля.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 26.07.2023 10:52 rg45 . Предыдущая версия . Еще …
Отредактировано 26.07.2023 10:49 rg45 . Предыдущая версия .
Re: Может я чего-то не понимаю....
От: Sm0ke Россия ksi
Дата: 26.07.23 11:48
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Знакомясь с возможностями C++20 наткнулся на статью здесь

BFE>Предлагается решить задачу:
BFE>[q]
BFE>Допустим, есть вектор с целыми числами, и требуется поделить все его числа на два. Те, которые не делятся, нужно просто выкинуть.

Вариант с алгоритмами: https://godbolt.org/z/T4Ya6q58T

auto v_pred = [](my_item p) { return p % 2 == 0; };
auto v_mod = [](my_item p) { return p / 2; };
std::ranges::copy(
  std::ranges::filter_view(v_src, v_pred) | std::views::transform(v_mod),
  std::back_inserter(v_dest)
);


Деление и остаток в некоторых платформах делается за одну инструкцию, так воспользуемся std::div()

auto v_mod_1 = [](my_item p) { return std::div(p, 2); };
auto v_pred = [](const std::div_t & p) { return p.rem == 0; };
auto v_mod_2 = [](const std::div_t & p) { return p.quot; };
std::ranges::copy(
  std::ranges::transform_view(v_src, v_mod_1) | std::views::filter(v_pred) | std::views::transform(v_mod_2),
  std::back_inserter(v_dest)
);


Теперь Обычным Циклом:

for( auto it : v_src ) if( std::div_t res = std::div(it, 2); res.rem == 0 ) { v_dest.push_back(res.quot); }


Циклом, но без div():

for( auto it : v_src ) if( it % 2 == 0 ) { v_dest.push_back(it / 2); }


Но это же надо мерить по производительности
Отредактировано 26.07.2023 11:50 Sm0ke . Предыдущая версия .
Re[2]: замеры)
От: Sm0ke Россия ksi
Дата: 26.07.23 12:14
Оценка:
Замерил на годболте через chrono: https://godbolt.org/z/dK4jsoxG3
На массиве с 90к элементов

for: 201us
for: 138us
algo: 198us
algo: 130us
for div: 349us
for div: 363us
algo div: 587us
algo div: 508us


Не знаю на сколько можно доверять этим цифрам, ведь они менялись при перестановке способов в одной программе.
Отредактировано 26.07.2023 12:20 Sm0ke . Предыдущая версия . Еще …
Отредактировано 26.07.2023 12:15 Sm0ke . Предыдущая версия .
Отредактировано 26.07.2023 12:14 Sm0ke . Предыдущая версия .
Re[5]: Может я чего-то не понимаю....
От: B0FEE664  
Дата: 26.07.23 12:29
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP> До С++20 нельзя сделать std::transform без создания выходного массива.

Почему так нельзя?:
std::transform(numbers_in.begin(), numbers_in.end(), numbers_in.begin(), [](int x){ return x/2;});
И каждый день — без права на ошибку...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.