Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Даже во многих современных программах на C++ часто вижу int/short/long там, где по смыслу должно быть беззнаковое целое.
В этом и проблема: есть "математический" взгляд, т.е. "по смыслу", а есть практика.
На практике знаковые числа куда практичнее.
Ну вот пример: есть массив. Казалось бы, индексы могут быть исключительно 0...10 (к примеру). Но вот придумал разработчик языка, что можно индексировать с конца списка как arr[-2] — и всё, теория летит к чертям!
Или другой пример: откуда-то извне к нам поступают int'ы и если они попадают в диапазон индексов нашего arr[0...10], то надо взять значение из массива. Очевидно, что проще сделать проверки на знаковом целом и потом залезть в массив, чем сразу же делать отлуп вызывающему: "эй, подавай мне только беззнаковые! математику не учил штоле?".
Собственно, по этой же причине non-null ref — абсолютно тупорылая затея, ибо "следит" за косяками индусских мартышек, но при этом напрочь ломает код, который далеко не везде должен заботиться о null.
N>>Ну я больше имел в виду варианты типа (for(...;i-->0;...). Если это все случаи, то их мало и я на них таки не натыкался. Ядро таки огромное, 1148 это копейки, можно ни разу не встретить. Но учту. EP>Ну я говорю всё время про while(n--), но можем конечно и вариант с for погрепать. Но суть от этого не меняет — применяется повсеместно.
Для полноты картины:
linux-master $ grep -r 'for\s*(.*;\s*[[:alnum:]_]*\s*--\s*;\s*)' | wc -l
148
linux-master $ grep -r 'for\s*(.*;\s*[[:alnum:]_]*\s*--\s*;\s*)' | head -n25
linux-master/arch/arm/mach-sa1100/assabet.c: for(i = 100; i--; ) /* Read GPIO 9:2 */
linux-master/crypto/async_tx/raid6test.c: for (i = disks; i-- ; ) {
linux-master/drivers/base/component.c: for (i = master->match->num; i--; )
linux-master/drivers/crypto/img-hash.c: for (; i--; )
linux-master/drivers/crypto/mediatek/mtk-aes.c: for (; i--; ) {
linux-master/drivers/crypto/mediatek/mtk-aes.c: for (; i--; )
linux-master/drivers/crypto/mediatek/mtk-platform.c: for (; i--; ) {
linux-master/drivers/crypto/mediatek/mtk-sha.c: for (; i--; )
linux-master/drivers/crypto/mediatek/mtk-sha.c: for (; i--; )
linux-master/drivers/crypto/mediatek/mtk-sha.c: for (; i--; )
linux-master/drivers/crypto/stm32/stm32-hash.c: for (; i--; ) {
linux-master/drivers/crypto/stm32/stm32-hash.c: for (; j--;)
linux-master/drivers/gpu/drm/i915/gem/i915_gem_context.c: for (i = GEN8_3LVL_PDPES; i--; ) {
linux-master/drivers/gpu/drm/i915/gt/intel_lrc.c: for (n = execlists_num_ports(execlists); n--; ) {
linux-master/drivers/gpu/drm/i915/intel_runtime_pm.c: for (n = rpm->debug.count; n--; ) {
linux-master/drivers/gpu/drm/i915/selftests/i915_buddy.c: for (order = max_order; order--; ) {
linux-master/drivers/gpu/drm/i915/selftests/i915_buddy.c: for (order = top; order--; ) {
linux-master/drivers/md/raid1.c: for (j = pi->raid_disks ; j-- ; ) {
linux-master/drivers/md/raid1.c: for (i = pi->raid_disks; i--; ) {
linux-master/drivers/md/raid1.c: for (j = vcnt; j-- ; ) {
linux-master/drivers/md/raid1.c: for (i = conf->poolinfo->raid_disks; i--; ) {
linux-master/drivers/md/raid10.c: for (j = nalloc ; j-- ; ) {
linux-master/drivers/md/raid10.c: for (j = conf->copies; j--; ) {
linux-master/drivers/md/raid5-cache.c: for (i = sh->disks; i--; ) {
linux-master/drivers/md/raid5-cache.c: for (i = sh->disks; i--; )
...
Здравствуйте, rg45, Вы писали:
R>Ну и традиционное обилие аргументов. На вопрос "почему так нельзя делать" один отвечает: "потому что не пройдешь ревью", другой — "потому что за это можно получить тапком". Вас где так надрессировали, коллеги?
Нужно понимать, что код пишется для человека. Он не должен вызывать изумление, он должен быть понятен сразу.
Есть идиома for — её программисты уже воспринимают мгновенно: "так, вот откуда начинаем, вот докуда едем, вот шаг итерации". Выпадение любого из кусочков замедляет восприятие; а перемещение шага итерирования в проверку условия вообще сбивает с толку. Именно поэтому можно получить отказ в ревью, или тапком — там, где ревью не заведено.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Нужно понимать, что код пишется для человека. Он не должен вызывать изумление, он должен быть понятен сразу.
С каждым словом согласен.
S>Есть идиома for — её программисты уже воспринимают мгновенно: "так, вот откуда начинаем, вот докуда едем, вот шаг итерации". Выпадение любого из кусочков замедляет восприятие; а перемещение шага итерирования в проверку условия вообще сбивает с толку. Именно поэтому можно получить отказ в ревью, или тапком — там, где ревью не заведено.
Идиоме while(n--) столько же лет, сколько самому языку C. Тем, у кого эта конструкция вызывает затруднение, просто нечего тут делать.
А вариант for (size_t n = size(v); n--; ) отличается только тем, что сужает область видимости переменой цикла, вот и все.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
N>>Эти "субъективные предрассудки" превращаются в объективные, когда начинаешь смотреть, как воспринимает такой код не только тот, кто всю жизнь "в системе", как я, а и, например, типовой юниор, часто к тому же перешедший с какого-то соседнего языка. EP>Ну давай рассмотрим этот сценарий. Как по-твоему ведёт себя юниор впервые увидевший while(n--)?
Или заплачет и спрячется под стол (реально, будет делать вид, что всё понимает, пока носом не ткнёшь), или разберёт и поймёт логику.
Это всё очевидно. Неочевидно сходу то, насколько скоро он это всё забудет — а если такая идиома встречается не часто, то забудет быстро.
EP>Это пока шаблона while(n--) нет в голове.
С одного раза не запомнится.
EP>Да и что означает closed_range в данном случае? запрет на n=0?
Наоборот, что оба граничных значения включены в набор.
Closed — оба конца включены. Open — оба конца не включены.
Half-open range — например, в C++ для любого контейнера begin() входит в значения, а end() — нет. В Python, аналогично, range(0, 10) это от 0 до 9 включительно (а чтобы от N до M включительно вниз, надо писать range(N, M-1, -1)).
EP>Ну я говорю всё время про while(n--), но можем конечно и вариант с for погрепать. Но суть от этого не меняет — применяется повсеместно.
Вот именно что "повсеместность" этого подхода, как оказывается, сильно однобока.
Разобрать-то я его могу, не вопрос, но если за всё это время не встречал как систематическую идиому — это что-то значит.
EP>Да, сам увидел — я это место дополнил: EP>Точнее one-before-last обходится переносом декремента на первую строчку тела (как уже показывали ранее в этой теме): while(it != first){--it; ...}, но концептуально то же самое.
Ну и снова получаем уже известную проблему: надо перед циклом начать с n+1, чтобы в цикле работать с n. Это уже второй уровень наворота идиомы (первый — формат условия продолжения с пост-декрементом).
N>>Оптимизатор всё равно для типовых случаев вроде плоского массива всё это заоптимизирует. EP>Не всегда варианты простые. Тем не менее — прямее сразу делать оптимально по-возможности жеж.
Так не оптимально же, когда лишний инкремент на входе. Оптимально только если n уже количество элементов с номерами от 0 до n-1. Да, тогда можно пользоваться напрямую. Иначе удобнее через постпроверку.
N>>А через ФВП всё равно получится менее удобно, чем при поддержке foreach самим компилятором. EP>Ну это скорее вопрос к синтаксису замыканий и их возможностей. Радикальное отличие for_each+замыкание от range-based-for — только в return/break/continue, остальное синтаксический сахар.
Здравствуйте, netch80, Вы писали:
N>Это всё очевидно. Неочевидно сходу то, насколько скоро он это всё забудет — а если такая идиома встречается не часто, то забудет быстро.
Ну не запомнит юниор с первого раза, в чём трагедия?
N>>>Имеет, да. Но читаешь всё равно шаблонами. И шаблон типа for i in closed_range(n-1, 0, -1) будет читаться проще всегда, чем while(n--). EP>>Да и что означает closed_range в данном случае? запрет на n=0? N>Наоборот, что оба граничных значения включены в набор.
Ну то есть ты действительно имеешь в виду стандартное значение closed, а не какое-то своё, а значит [n-1, 0] так?
n=1: [0, 0]
n=0: [-1, 0]
Зато смотри как стройно с while(n--): (n, 0]
N>Closed — оба конца включены. Open — оба конца не включены. N>Half-open range — например, в C++ для любого контейнера begin() входит в значения, а end() — нет. В Python, аналогично, range(0, 10) это от 0 до 9 включительно (а чтобы от N до M включительно вниз, надо писать range(N, M-1, -1)).
Ну так в C++ он half-open, как раз для того чтобы пустые диапазоны можно было определять: [first, first).
Как ты пустой диапазон обозначишь на closed range? [first, first] не работает
На Python он тоже half-open.
А у тебя closed_range(x, y, -1) — это что? [x, y]?
EP>>Ну я говорю всё время про while(n--), но можем конечно и вариант с for погрепать. Но суть от этого не меняет — применяется повсеместно. N>Вот именно что "повсеместность" этого подхода, как оказывается, сильно однобока.
Что значит однобока? Само по себе итерирование вниз нужно очень редко, по моим прикидкам один к ста или даже один к тысячи по сравнению с итерированием вверх
EP>>Да, сам увидел — я это место дополнил: EP>>Точнее one-before-last обходится переносом декремента на первую строчку тела (как уже показывали ранее в этой теме): while(it != first){--it; ...}, но концептуально то же самое. N>Ну и снова получаем уже известную проблему: надо перед циклом начать с n+1, чтобы в цикле работать с n. Это уже второй уровень наворота идиомы (первый — формат условия продолжения с пост-декрементом).
Да не нужно начинать с n+1, оно уже "готовое" — итератор .end() — уже указывает на один за последним элементом.
То есть для стандартных [first, last) получаем: while(last != first){--last; use(*last); }, никаких преикрементов.
Точно также как для N элементов последний индекс начиная от 0 — N-1, поэтому не нужно делать N+1 перед циклом
N>>>Оптимизатор всё равно для типовых случаев вроде плоского массива всё это заоптимизирует. EP>>Не всегда варианты простые. Тем не менее — прямее сразу делать оптимально по-возможности жеж. N>Так не оптимально же, когда лишний инкремент на входе
Здравствуйте, Evgeny.Panasyuk, Вы писали:
N>>Это всё очевидно. Неочевидно сходу то, насколько скоро он это всё забудет — а если такая идиома встречается не часто, то забудет быстро.
EP>Ну не запомнит юниор с первого раза, в чём трагедия?
Трагедии нет тут нигде. А вот регулярное неудобство даже для "сеньора" — факт.
N>>>>Имеет, да. Но читаешь всё равно шаблонами. И шаблон типа for i in closed_range(n-1, 0, -1) будет читаться проще всегда, чем while(n--). EP>>>Да и что означает closed_range в данном случае? запрет на n=0? N>>Наоборот, что оба граничных значения включены в набор.
EP>Ну то есть ты действительно имеешь в виду стандартное значение closed, а не какое-то своё, а значит [n-1, 0] так? EP>n=1: [0, 0] EP>n=0: [-1, 0]
EP>Зато смотри как стройно с while(n--): (n, 0]
Во! И вот тут мы пришли к тому, почему плохо с сишными беззнаковыми: [-1, 0, step=-1] — это просто пустое множество. А если его тупой казённый заворот превратил в [UINT_MAX, 0, step=-1] — то мы пробежимся по всем значениям.
Вариант же while(n--) работает только в том случае, если n — количество элементов. Но тогда проблема возникает в другом — что это количество должно быть гарантированно не больше UINT_MAX, что легко нарушить, например, имея какое-то внешнее хранилище, или сдуру применив unsigned int вместо sizeof на 64-битке.
EP>Ну так в C++ он half-open, как раз для того чтобы пустые диапазоны можно было определять: [first, first).
Не только. Ещё удобно их делить и суммировать: [0, 10) делится на [0, 6) и [6, 10).
Но это всё не абсолютные аргументы, а только части их.
Проблема разворота такого диапазона отлично видна на странностях реализации reverse iterator.
EP>Как ты пустой диапазон обозначишь на closed range? [first, first] не работает EP>На Python он тоже half-open. EP>А у тебя closed_range(x, y, -1) — это что? [x, y]?
Да, при подсчёте с уменьшением (при x<y должно стать пустым множеством).
EP>>>Ну я говорю всё время про while(n--), но можем конечно и вариант с for погрепать. Но суть от этого не меняет — применяется повсеместно. N>>Вот именно что "повсеместность" этого подхода, как оказывается, сильно однобока.
EP>Что значит однобока? Само по себе итерирование вниз нужно очень редко, по моим прикидкам один к ста или даже один к тысячи по сравнению с итерированием вверх
Может, потому и не запомнилось. Но я помню места, где такое итерирование делалось в менее экстравагантном стиле — через стандартный for или while, где постдекремент в условии не делался. Значит, их встречал чаще.
N>>Ну и снова получаем уже известную проблему: надо перед циклом начать с n+1, чтобы в цикле работать с n. Это уже второй уровень наворота идиомы (первый — формат условия продолжения с пост-декрементом). EP>Да не нужно начинать с n+1, оно уже "готовое" — итератор .end() — уже указывает на один за последним элементом.
Это если у тебя n уже количество элементов. О чём я и говорю. А если это что-то другое — то идиоматичность ломается.
Здравствуйте, CreatorCray, Вы писали:
CC>Все эти извраты, типа operator , можно использовать строго там где без них ну ваще никак и хорошенько комментировать. CC>Потому что это источник проблем.
Вот это твое "можно использовать" все портит
CC>Это в КСВ то?
Ах, да, я и забыл, что rsdn уже не тот, что был в 2004.
Здравствуйте, wander, Вы писали:
W>Вот это твое "можно использовать" все портит
Лучше совсем не использовать, да.
Но нет нет да и вылазит ситуёвина что с ними получается лучше чем без них и тогда пишется ровно одно место, заворачивается в макру, пишется портянка комментов объясняющая как мы дошли до жизни такой и скрепя сердце юзается.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
PM>>размера и позиции в файловых потоках. CC>За это лично готов проводить массовые расстрелы. Мне такие проектировщики крови попортили уже.
Да, удивительно видеть int для этого в 21-м веке. Похоже, что это можно списать на ошибки молодости, потому что в capnproto автор уже использует size_t для размеров буферов и файлов. Ну зато наверно он деревья на доске хорошо вращал и мячики для гольфа в автобусе правильно посчитал.
Здравствуйте, rg45, Вы писали:
R>А вариант for (size_t n = size(v); n--; ) отличается только тем, что сужает область видимости переменой цикла, вот и все.
Для компилятора нет никакой разницы. Для человека — есть.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, Evgeny.Panasyuk, Вы писали:
N>>Ну я больше имел в виду варианты типа (for(...;i-->0;...). Если это все случаи, то их мало и я на них таки не натыкался. Ядро таки огромное, 1148 это копейки, можно ни разу не встретить. Но учту. EP>Ну я говорю всё время про while(n--), но можем конечно и вариант с for погрепать. Но суть от этого не меняет — применяется повсеместно.
Претензии как раз к (for(...;i-- >0;...)
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, netch80, Вы писали:
N>Трагедии нет тут нигде. А вот регулярное неудобство даже для "сеньора" — факт.
С while (i--)? Не, для senior тут всё ок.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, wander, Вы писали:
W>Вот ты думаешь я какую точку зрения здесь представляю?
Свою разумеется.
Мне в общем то всё равно.
Все эти срачи — высказывание мнений. Никто никого тут никогда не переубедит напрямую. Но в спорах рождается истина, для каждого своя, так что бывает интересно посмотреть на аргументацию каждой стороны — глядишь что то новое или интересное проскочит.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока