- 1
std::ranges::for_each(tasks, [](auto x) {x();});
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
+1
std::ranges::for_each(tasks, [](auto x) {x();});
а ещё теперь не нужно писать std::begin(), std::end().
c++ будущего.
Foreach = декларативность, функциональность, молодость, стиль
А форыч это, всё таки, Черч
но ленивости (генеаторов) всё равно не будет из коробки, угу
можно как-бы сделать (псевдокод)
будет как питон с его map и прочим itertools
но в питоне есть генераторы, а тут нету, и если питухов 100000 то багор
> take_first(10, sort(&sorter, petuhi.begin(), petuhi.end()))
Только тут же возникает проблема: что возвращает sort() и что принимает take_first()? В итераторном коде сигнятура take_first() выглядела бы примерня как "(void) take_first(size_t count, It begin, It end, OutIt outBegin)", и в неё sort() някак ня впихнуть.
А вот благодаря миксиняшкам такие проблемы решаются тривиальня.
но это всё не лениво, а значит весьма сомнительно
Так вот имення чтобы ня принимать коллекцию и делались итераторы. Чтобы ты мог, няпример, легко и просто отсортировать половину коллекции: "std::sort(collection.begin() + collection.size() / 2, collection.end())".
А ещё так нячинают возникать проблемы: а что, если я хочу получить из sort() ня vector, а, няпример, list? А что делать с кастомными аллокаторами? А что, если я хочу в take_first засунуть map, а получить vector? И так далее.
Вот чтобы таких проблем ня было — и придумали итераторы. Ты сам создаёшь контейнеры, которые тебе нужны, а функции оперируют только няд итераторами. Но, как показала практика, это была далеко ня самая лучшая идея.
имхо, итератор это хорошо, просто небыло красивых средств для ленивости, и потому половина функционала была недоступна..
а вот твой пример с /2 отлично работал же, да и независящие от контейнера алгоритмы -- тоже
2. Отсутствие чейнинга. Тут всё и так понятно: вместо няшного sort().filter().map().reduce() приходится писать гигантские вербозные портянки.
3. Няобходимость в std::(back_|front_)?inserter для вставки результата в изнячальня пустой/маленький контейнер.
4. Общая высокая вербозность, особення в базовых (в том числе и самых распространённых) случаях. Вместо collection.sort() / sort(collection) — sort(collection.begin(), collection.end()).
Особенно сраная пара remove/erase, про которую так любят спрашивать на собеседованиях.
один питух двигает говно, а второй чистит вилкой освободивщееся место если я верно помню
К — Консистентность.
да, это отсос. В руби я могу сделать цепочку от 0 до бесконечности, и выбрать из нее по условию первые N элементов, а тут фиг
>Отсутствие чейнинга.
угу( ну вот это как раз как в питоне тоже, и это плохо. А вот в руби... ну ты понял)
> std::(back_|front_)?inserter
А иначе как контейнер расширится?
Итератор в векторе или массиве же это просто указатель на элемент ЗА хвостом, верно?
Как же можно сделать так, чтобы при писании туда вектор бы расширялся?
>Общая высокая вербозность
Угу(( но без итераторов было бы еще вербознее
Ну вот поэтому range -- это более красивая концепция чем итератор.
но и итератор на момент с03 тже было круто имхо
А в ranges вообще ничего ня надо расширять. После серии преобразований ты получаешь ленивый генератор, который вообще някуда записывать ня надо: ты можешь итерироваться сразу по няму.
Но даже если вдруг понядобилось материализовать — ты просто пишешь условный ... | ranges::to<std::vector> и получаешь всё в лучшем виде.
Да это миф, на самом деле. Я вот сходу даже не назову алгоритм, который ну совсем прям на любом контейнере работает.
Везде или какие-то подвохи с вычислительной сложностью std::find(map.begin(), map.end(), x) vs map.find(x) или какой-то постпроцессинг в духе v.erase(std::remove(v.begin(), v.end(), x), v.end()) нужен.
Ну, они обычно РАБОТАЮТ. Стоит ли так работать, или нет — это другой вопрос.
просто итераторов бывает много разных: кто-то поддерживает арифметику, кто-то поддерживает запись, итд
* На всех кастомных контейнерах, которые косплеят соответствующий стандартный.
А поскольку в стандартной либе все контейнеры разные, каких-то общих алгоритмов у них очень мало получается. По сути каждый алгоритм под что-то конкретное нужен. И во многих местах вместо них надо брать кастомную версию из самого класса и т.п. Консистентность на высоте.
Короче не особо взлетела эта идея, имхо.
Это был бы булщит уровня "локальный вызов заменим удаленным вызовом по HTTP, а алгоритм останется тем же" (над этим еще Фаулер ржал в 2003-м), но какие-то алгоритмы всё таки можно переиспользовать
А какие? Ну вот банальный find. Он работает, по сути, только на листе и не отсортированном векторе. Для мапы/хешмапы надо брать их реализацию, которая за логарифм ищет. А для отсортированного вектора есть binary_search, который на отсортированном листе бесполезен.
Так и живём.
Я пишу алгоритм, который итерируется по нему и выводит значение если оно полдходит по условию. Что не так?
Про find: с этим даже джависты отсосали: там вроде поиск в массиве работает только если он сортирован, именно ради бинари сёч
но если мне похуй на перфоманс, я могу написать поиск за O(N) и он будет работать на чем угодно, не?
Но тогда теряется смысл в специализированных контейнерах. С тем же успехом можня хранить всё в std::vector.
А вдруг я ищу что-то в алгоритме раз в 10 лет, а всталяю в середину каждые 1ms, и тогда мне важно взять именно list?
Ну ок, transform/copy действительно для всех контейнеров работают (на входе). Целых джва алгоритма уже нашли!
форич/_н
count/count_if
mismatch
adjacent_find
search/_n
Все что требует не лучше ForwardIterator
search (не путать с find!) на мапе или хешмапе очень полезен, конечно... Хотя иногда что-то и найдёт, наверное.
(╥﹏╥)
(╥﹏╥)
Действительно плохой алгоритм.
Это просто ещё один пример потёкшей абстракции.
Спрятать алгоритмы в сами контейнеры, потому что независящий от контейнера алгоритм часто просто не написать?
А мне вот нравится, что у вектора и умассива общие алгоритмы, а в джаве -- нет..
А вот взять например метод sort, который есть у std::list и у std::forward_list. А у вектора никакого метода sort нет, используйте std::sort.
Зачем? Зачем? std::sort требует random access iterator и поэтому для std::list и std::forward_list нельзя std::sort, а для std::vector можно? А почему тогда не сделать так, чтоб и у std::vector был метод sort, ну, типа для единообразия?
> Учителям такой категории вообще, судя по всему, всё до лампочки: в C++ используется библиотека STL, а значит, надо рассказать ученикам STL; разумеется, дальше vector'а и list'а обучение никогда не заходит (при этом эти два контейнера, пожалуй, самые бесполезные из всего STL), но самое интересное, что ученики, разумеется, так и не понимают, о чём идёт речь. В самом деле, как можно объяснить разницу между vector и list человеку, который никогда в жизни не видел ни динамических массивов, ни списков и вообще не понимает, что такое указатель? Для такого ученика list отличается от vector тем, что в нём нет удобной операции индексирования (почему её там нет? ну, нам что-то объясняли, но я ничего не понял), так что вообще-то всегда надо использовать vector, ведь он гораздо удобнее. Что? Добавление в начало и в середину? Так оно и для вектора есть, какие проблемы. Ну да, нам говорили, что это «неэффективно», но ведь работает же! Переучить такого ученика практически нереально: попытки заставить его создать односвязный список вручную обречены на провал, ведь есть же list, а тут столько ненужного геморроя! Собственно говоря, всё: если нашему обучаемому дали в руки STL раньше, чем он освоил динамические структуры данных, то знать он их уже не будет никогда; путь в серьёзное программирование ему, таким образом, закрыт.
Имення поэтому учить программированию нядо с C.
^_^
хуже только сразу учить их на каком-нить питоне или JS.
В случае крестов они хотябы будут знать, что бывают массивы, а бываюи сваязанные списки
В случае питона они не будут знать ничево, и не отличать объект от указателя
Именно поэтому я за "РНР".
Только выкинуть, и нового родить
Контейнеры-хуёйнеры какие-то блядь. Как всё сложно. Предлагаю кложуру и хэшмап
https://youtu.be/b-Eq4YV4uwc?t=1607
https://youtu.be/b-Eq4YV4uwc?t=1724
https://www.youtube.com/watch?v=ouUloBNBhOA
> И std::kokoнтейнеров из крестоговна тоже нет.
Тебе ня придётся учить стандартную библиотеку, если каждый раз ты пишешь стандартную библиотеку сам╰(▔∀▔)╯!
Стандартная библиотека крестоговна мне там банально не подходит т.к. там требуется какой-то хип, а у меня в контроллерах может не быть никакого хипа (если всё решаемо статической памятью), а еще там какие-то исключения RTTI и прочая такая питушня, которая мне в контроллерах няхуй не нужна. Да и в целом можно поднасобрать каких-то реализаций различной питушни на сишечке с гитхаба, и ее использовать вместо крестовой говностдлибы.
Зачем?
> исключения RTTI
-fno-exceptions -fno-rtti
> Да и в целом можно поднасобрать каких-то реализаций различной питушни на сишечке с гитхаба, и ее использовать вместо крестовой говностдлибы.
И собрать свою говнолибу.
> Зачем?
Память выделять там, например для какой-нибудь там std::list питушни. Без хипа я могу например так сделать: https://govnokod.ru/23275
Ну в бусте вроде были такие контейнеры... И интрузив списки, которые сишники любят делать руками тоже были.
А стд, конечно, очень сильно завязано на хип и исключения. 90% стандартной либы можно тупо выбросить под фристендингом.
Ну а вообще в том посте roman-kashitsyn всё правильня расписал.
А как репортить ошибку, когда в нём место закончилось? Рушить абстракцию и приделывать аллокатору какое-нибудь has_enough_space()?
Ну заебись для эмбеддеда... На какую-нибудь опциональную хуйню для UI памяти не хватило, весь чип ушёл в ребут.
Настоящие цари заранее должны знать, что им там всего хватит под все возможные сценарии. Если вдруг памяти не хватает, это баг, и надо менять структуру или допаивать ОЗУ или менять контроллер
> Железка может уметь много чего делать, и ей может быть не нужно делать это одновременно (и памяти на одновременное делание всего у нее тоже нет). Поэтому железка может выделять память под някню№1, сделать някню№1, освободить память, выделить память под някню№2, сделать някню№2, освободить память. И эти хуйни№x может быть много, некоторые не сильно жрущие оперативку хуйни могут вполне работать одновременно, и они могут быть заранее неизвестны т.е. загружаться с внешнего флеш-накопителя или по блютузу какому-то скачиваться и устанавливаться, если это какая-то IoT-няшнота. Так что для каких-то задач это вполне себе пригождается.
https://govnokod.ru/27391#comment625630
А аллокатор может возвращать uint8_t ? Ну типа это память не вообще, а память, выделенная в контексте некоторой арены, где мы адресуемся не по байтикам, а по блокам в 16 байтиков, и этот возвращаемый uint8_t это смещение относительно какого-то там указателя в этой арене, который надо еще на 16 умножить и прибавить к этому базовому адресу чтоб получить реальный адрес
Не, он хочет что-то в духе кастомного std::vector::pointer. Чтобы там не настоящий поинтер был, а какой-то компактный тип, который можно через какую-то арифметику превратить в настоящий.
З.Ы. Ну кстати можно, походу. Этот тип из аллокатора берётся как раз. Если он будет вести себя как поинтер, то вектор схавает.
А если не уменьшается, и если я не хочу хранить полный указатель под каждую хрень? Есть один настоящий указатель, и куча смещений от него, и эти смещения влезают uint8_t без проблем.
Ну там в трейтах аллокатора можно указать свой тип для pointer. Может и прокатит что-то кастомное, если прикрутить звезду и скобочки.
Просто uint8_t нельзя, конечно. Но структурка или енум с uint8_t могут прокатить.
Трейты-хуёйты, может и прокатит, а может не прокатит... Ясно всё.
Сколько надо будет с этим примерно ебаться, и много ли я от этого выиграю на своих задачах?
Х.з., я тут только пессимистичный прогноз могу дать. Наебавшись с тем же boost intrusive списком, я тупо выбросила его няхуй и написала свой за 5 минут.
Кстати, std::list хранит ня указатели, а Allocator::pointer, так что можня попробовать упороться с таким аллокатором. Но лучше, конечно, использовать нярмальные intrusive list.
Списку, который на основе этого аллокатора работает, нужны указатели. Ну и тратить на них по 4 байта не хотелось бы, если список маленький.
> нярмальные intrusive list
Их, кстати, довольно сложно юзать в режиме "несколько списков через одни и те же объекты"... При том, что внутри там ерунда по переключению линков, которая пишется буквально за 5 минут (и в ней даже условий нет, так что юнит-тест элементарен). Ну в данной конкретной задаче может быть и прокатят.
Да, ссылки ты размещаешь в объекте сам и поясняешь либе как ими пользоваться.
Так-то ты мог свой итератор со своим ++ написать и в 03 еще
Скорее оставляешь управление памятью за собой, как в сишке. А "контейнер" даёт тебе attach/detatch вместо insert/remove.
В теории могло бы быть полезно, когда тебе надо 2-3 списка построить через одни и те же ноды. Но на практике мне не удалось запинать эти злоебучие трейты, пришлось решать эту задачу руками.
Делаешь один связанный список, и по нему строишь два других списска: один напрямую, другой через один, например?
Это всё решается и на обычных контейнерах, конечно. Но по памяти/пирфомансу будет совсем не айс.
Тем, что придется обёртку делать на каждый такой указатель?
Ну получается слишком много мелкой хуйни и аллокаций для неё. Если у тебя какие-нибудь треды тыщи раз в секунду просыпаются и засыпают, как-то стрёмно аллокатор дрочить каждый раз. Особенно если в этих местах аллокатор вообще трогать нельзя.
А с интрузивом оно интегрировано в основную структуру. Ну как сишник бы сделал.
А в самих "контейнерах", по сути, только башка этих списков/деревьев.
Спасибо, ты прям вернул говнокод 2010-го.
В крестах, кстати, есть std::make_shared(). По Стандарту std::make_shared() обязана делать только одну аллокацию, сшивая управляющий блок с данными. Так что shared_ptr, сделанный через make_shared(), и shared_ptr, сделанный через конструктор — две очень большие разницы.
блядь, какой С++ сложный
шарик помрет, потому что померло всё выражение, а Bar никто не делитнул, вот он и остался, верно?
шарик тут суть вообще временный обхект же
Во всяком случае, так было раньше, а теперь уже всё отличня.
> Начиная с c++17 утечка памяти в том хитром редком примере, ради которого в STL был добавлен std::make_shared, уже невозможна.
Проблема в том что этот ресурс — по сути дела struct { char[8294400] }.
> make_shared
Какой эпичный выстрел в ногу!
а потом дальние и ближние указатели
Которая не будет всякую ненужную питушню треботвать, и будет эффективна по потреблению памяти.
> раз ты пишешь стандартную библиотеку сам
"В Go распространен подход к выполнению большенства рутинных задач явно, уменьшая сложность понимания кода"
буду теперь форсить эту фразу везде
Что понимается под "нормальным"? Убожество как в джаве, дотнете или скриптушне, которое только для последовательного обхода пригодно?
Всё-таки крестовые итераторы пытались решить большую задачу, чем просто обход контейнера по порядку.
Пытались )))
А какой должен быть нормальный итератор? Какую глобальную проблему решили итераторы в крестах, которую в скриптушне решить не смогли? Допустим, в питоне.
Указать на слайс контейнера чтобы удалить его или заменить на что-то другое, к примеру. Или (осторожно) удалять элементы из контейнера прям во время его обхода. Или найти элемент в мапе и записать в него новое значение не делая повторный поиск. Или даже проделать над ними какую-то арифметику(!), если random access.
В питоне, емнип, имея итератор ты вообще ничего не можешь сделать. Ну кроме как достать из него следующее значение.
Итераторы в крестах предназначались, как точка кастомизации. Захотел сделать итератор, который проходит по вектору строк и выдаёт только слова, имеющиеся в словаре, приведённые к нижнему регистру. Или проходит сначала по нечётным, а потом по чётным индексам — сделал, передал в имеющуюся функцию и всё работает.
Ну просто для обхода хватило бы и убогого forward iterator'а, которым хвалятся джавы да питоны.
Хотя... их итератор даже на forward не тянет т.к. его нельзя передать в какой-нибудь erase() или заюзать как границу для последующего поиска.
Но это не важно.
Важно, что они итераторы своего конца никогда не знают, и потому всякие крутые штуки как в крестах с ними не сделать
Такое есть в скрипушне, а ещё в «Nim», кажется.
> Или (осторожно) удалять элементы из контейнера прям во время его обхода.
Такого, кажется, в скриптушне действительно нет. Раньше же итератор протухал как только его коллекцию как-либо меняли? Тогда крутая фича.
Хотя нет, в питоне можно вот так:
Это ведь итератор работает!
> В питоне, емнип, имея итератор ты вообще ничего не можешь
Это правда лишь отчасти. В питоне есть слайсы, генераторы и итераторы, которые делают всякие приколы. Я правильно понял, что «Ranges» просто совместил в себе все эти финтифлюшки? Справедливо ли утверждение, что крестобляди соснули, т.к. им пришлось комюпировать функционал из скриптушни? Или это скриптобляди поспешили и людей насмешили, а крестобояре обстоятельно подумали и сделали как надо?
Это удаление элемента по знячению. Об эффективнясти такого можня догадаться.
По идее нужно найти элемент за O(N) (если нет требования сортированности массива) и, удалив элемент, двигануть пол массива. Какой окшмар
если все, то логично сначала все найти, потом подвигать.
А нельзя явно заказать линкдлист вместо массива, если уж мы такие удалятели знатные?
> А нельзя явно заказать линкдлист вместо массива, если уж мы такие удалятели знатные?
Нят. Это слишком сложня для скриптов.
Именно потому я за перл: массив, хеш, да скляр. Всё
Берешь, и делаешь нужную структуру под конкретную задачу
Потому мне всегда смешно от сёмных нахрюков про "в си даже мапы нету"
вон сколько сложностей возникло в крестах из за STL контейнеров
А в позиксе есть! Правда она одна на весь процесс и удалять из неё нельзя. Но это мелочи.
там вроде и массив есть с байнари сёрчем... такие костыли, конечно
bsearch сишка
tsearch юникс
qsort сишка
ужасный раздрай) Правда, по заголовочному фалйу понятно
А в Win32 есть какинито алгоритмы интересные?
Или у MS позиция "юзайте STL, пишите на крестах"?
В С++ есть несколько категорий итераторов:
https://www.geeksforgeeks.org/introduction-iterators-c/
Самые тупые их них могут только выдавать значение (разыменовываться) или только двигаться вперед (++)
Самые продвинутые умеют даже random access.
Некоторые умеют писать или удалять данные
Все зависит от категории.
https://docs.microsoft.com/en-us/sql/relational-databases/cursors?view=sql-server-ver15
3ацени, кстати, сколько их типов разных
* Доступа к возвращаемому значению генераторов посредством итераторов не сделать (ведь это, о боже, изменит значение генератора)
* Ленивых итераторов быть не может (ведь доступ к значению заставит это значение изменится, какой ужас)
* Абстрагировать поток как коллекцию значений с помощью итератора не получится (поток меняется от чересчур пристального взгляда на него, кошмар).
И нахуя нужны такие итераторы?
Kiselyov zippers