Pull to refresh

Comments 54

UFO landed and left these words here
ИМХО, само условие как таковое, не должно зависеть от восприятия. Оно должно быть однозначным для любого читателя. Главное это разделение скобками условий на группы. Что-то на подобии как большая буква идет в начале предложения и точка в конце. Так и скобки должны отделять начало и конец некоторого условия. Тогда все неоднозначности уходят сами собой.
Кем? Компилятором или теми, кто работает с вами в команде/потом будет поддерживать код?
UFO landed and left these words here
Пишу всегда со скобками. На всякий случай. Как-то так спокойнее. :)
Недавно на хабре была статья, в которой говорилось, что написание лишнего кода «на всякий случай» — признак плохого программиста. Я не говорю что вы плохой программист, статья такая.
Там имелись в виду архитектурные решения, емнип.
Скобки — не совсем код. Для однозначного разделения приоритетов операций всегда использую скобки. Так более нагляднее где и что, особенно в длинных условиях.
ИМХО, лишние символы наглядности вовсе не способствуют.
если условие не длинное (как в приведённом примере) то скобки не имеют смысла
а если длинное и состоит из подусловий, то нагляднее будет каждое подусловие написать в отдельной строке
UFO landed and left these words here
Сперва проголосовал за первый вариант, потом понял, что напишу так:
if ((a != b || c != d) && !e) { }
Но ведь это, кажется, изменит смысл условия?
изменит. && имеет больший приоритет, чем ||
Верно, напутал. Ниже уже поправляют.

Потому скобки обязательно и ставлю, когда намешаны ИЛИ и И — чтобы смысл был однозначен.
Аналогично. Скобки расставляю так, чтобы был понятен смысл разделения условий на группы.
Вот кстати наглядный пример к моему комментарию ниже, ваше выражение не эквивалентно описанным, потому что приоритет && больше чем || и без скобок оно выполнится не так.
Тогда уж если сохранять приоритет операций, то
if ((a != b) || ((c != d) && !e) { }
У вас перед блоком 1 скобки не хватает закрывающей.
Я думаю идея ясна, а скобки переживут.
Вы поставили четыре открывающие скобки, но только три закрывающие. Это вызовет сообщение об ошибке.
Он показал саму суть: раз уж добавлять лишние скобки, то до конца — ведь в 1м примере голосовалки все равно может быть неоднозначное восприятие. Многим приходится программировать на разных языках и там приоритет операций может быть иным, так что перестраховщики будут ставить скобку после каждой операции
Ваш первый вариант добавляет избыточности, но не делает условие понятным,
потому что с приоритетами по группам легко, все помнят, что приоритет
сравнений больше чем у логических операторов.

Я пишу так:
if (a != b || (c != d && !e)) { }

Несмотря на то, что порядок приоритета логических операторов мне хорошо известен, это делает код читабельнее. Особенно в таком случае, когда операция с более высоким приоритетом стоит правее.
К сожалению помнят не все, но ваш пример делает код понятнее даже для этих людей.
Всегда без скобок из-за того что затрудняют понимание. Если же условие обретает форму, когда больше чем 2 под-условия, то разбиваю условие на несколько bool-евых флагов, которые читать проще.

пример разбития на несколько под условий:
bool fileOpen = (bla-bla-bla);
bool rc4used = (bla-bla-bla);
if ( fileOpen && rc4used )
{
// code
}

Я в таких случаях ещё добавляю const к объявлению.
Я пользуюсь еще интересным приемом (правда это немножко иной язык программирования: perl):
{
a == b or last;
d > x + 150 or last;
f < 16 or last;
# все условия выполнены, выполняем действие

}
Сильно смахивает на аналог конструкции:

{
  if (!...)
    goto end;
  if (!...)
    goto end;
  if (!...)
    goto end;

// code here

end:;
}


Поэтому у меня возникают сомнения касательно правильности такого подхода
return тоже «смахивает» на goto, но его никто в этом не упрекает. Есть четкий блок, внутри него условия, несовпадение условий — выход из блока. Если внимательно присмотритесь, то выход из блока используется сплошь и рядом: досрочное завершение подпрограммы с return, досрочный выход из цикла, case, иксепшины и т.д. Это нормальный прием. Сейчас объясню почему. Обычно, логика выполнения идет по одному пути: проверка, не выполнилась — свалили, проверка, не выполнилась — свалили, и т.д. Так происходит очень часто.

Например, банальная ситуация: проверили существование отправителя, нет — ошибка и конец, проверили достаточность денег на счету, нет — ошибка и коней, с получаетелем тоже самое и в конце запись в таблицу логов, нет — ошибка и конец. Если вы будете делать это через if, то создадите многоэтажную конструкцию, на 4й ступеньке ваш код перейдет в стадию «отвратный». Я практически никогда не допускаю больше 3х вложенностей.

Если же представить выполнение задачи по моему методу, то будет примерно следующее:

ok = 0;
{
проверка1 or last;
вычисления;
проверка2 or last;
вычисления;
проверка3 or last;
вычисления;
проверка4 or last;
ok = 1;
}
ok or rollback;

В такой конструкции явно прослеживается корректная цепочка операций, а все что не удовлетворяет ей — отсекается. Попробуйте, так очень удобно и понятно
Ну и в добавок хочу отметить, что также в коде использую еще одну практику:
Если текст условия больше чем 25 символов, то нужно либо посмотреть в название переменных, либо составить несколько bool переменных.

Ранее у программеров была эмпирическая оценка — не больше 86 символов на строку.

Пример:
if(boost::filesystem::file_size(fpath) < minPackedImageSize && dosHeader.e_lfanew<= boost::filesystem::file_size)
{
// code
}


1) sizeof(«boost::filesystem::file_size(fpath) < minPackedImageSize && dosHeader.e_lfanew<= boost::filesystem::file_size») больше 25 символов
2) sizeof(«boost::filesystem::file_size(fpath) < minPackedImageSize») больше чем 25!!!
3) sizeof(«dosHeader.e_lfanew <= boost::filesystem::file_size») больше чем 25

Этот доп. признак позволяет мне обратить внимание на плохо написанное условие с точки зрения эстетики, а следовательно и читабельности кода.

Разбиваю на:

int fileSize = (int)boost::filesystem::file_size(fpath);
bool isCorrectElfanew = dosHeader.e_lfanew <= fileSize;
bool isImageLarge = fileSize  > minPackedImageSize;

if( !isImageLarge  && isCorrectElfanew )
{
// code
}


т.е. благодаря этому признаку, я смог остановить на «паузу» свой мозг и посмотреть на этот «дурно пахнущий» кусок кода. В спешке я бы не заметил этого.
Всегда пишу со скобками. Что-бы потом не удивляться как копилятор или интерпретатор обрабатывает приоритеты операторов.
можно их (приоритеты) просто выучить
А я их знаю. Но я не уверен что их знает данный конкретный копилятор или интерпретатор. :)
для одного языка — это закон, но согласен — в разных языках есть свои нюансы
Когда пишешь на нескольких языках, то проще скобки поставить, чем «контекст переключать».
Лучше выделить в функцию, имя которой будет полностью оправдывать ее назначение.
Для 2-3 переменных — это лишнее, а для 4+ потом можно запутаться в порядке параметров.

Я бы долго бил за такое:
if (checkSomething(a, b, c, d, e)) { doSomething(); }

Учитывая, что даже человеческие названия переменных одинакового типа будут читаться не лучше «a, b, c, d, e».
if (checkSmth1(a,b) || (checkSmth2(c,d) && checkSmth3(e))) {}


:)
Согласен с автором, что сложное условие лучше выносить в функцию (функции). При этом одна функция не обязательно должна содержать одно условие. Например:

if (IsSmth1(a,b) || IsSmth2(c,d,e)) {}
...
bool IsSmth1(a, b) { return a != b; }
...
bool IsSmth2(c, b, e) { return c != d && !e; }


Возможно, для данного примера делать отдельные функции избыточно и скобок вполне хватит для улучшения восприятия кода.
UFO landed and left these words here
Код есть искусство. Он должен читаться легко и быстро. Логическое выражение с лишними скобками лишь вынесет мозг в попытке его прочитать, что замедлит работу. А без лишних скобок логика условия сразу будет понятной.
Обычно пишу со скобками, когда выражения визуально сложны для восприятия. Выражения со >> и << всегда заключаю в скобки, т.к. уже напарывался на неверную запись условия. На мой взгляд, в C и C++ перемудрили с приоритетами.
Приоритетам логических операций не очень доверяю, поэтому так:
if (a != b || (c != d && !e)) { }

Сравнения ставлю в скобки только в конструкциях вида

bool q=(a!=b);

или

bool x=(a<0? a<b: a>b);
if(  (a != b || c != d) && 
    !(x != y || z != w) &&
     (g != f || r != w) ) 
{ 
}
UFO landed and left these words here
может не очень в тему, но я обожаю писать конструкции типа a=(bla bla)?(bla bla):(bla bla bla);
так вот в таких случаях почти всегда очень помогают скобки

а в обычных случаях скобки редко использую, в основном только в сложных условиях
Приведенный пример я бы написал так:

if (a != b || (c != d && !e)) { }


Т.е. скобкаи я всегда явно выделяю порядок проверки условия.
Если бы не было паскаля, то все бы гарантированно знали, что в выражении (a != b) || (c != d) скобки не нужны.
За одно то, что в паскале логические операторы имеют приоритет над сравнением, Вирт будет гореть в аду.
А за то, что в C операции << и & имеют приоритет ниже, чем сложение? Хотя << это умножение, а & — остаток от деления.
Не знаю, стоит ли Вирту отвечать за это.
А если серьезно, то претензия не принимается, т.к. в конструкции a != b || c != d трактовка может быть только одна. Никому в голову не придет что имелось в виду a != (b || c) != d. Поэтому именно за то что в паскале автор языка заставляет ставить скобки в совершенно однозначной ситуации надо руки обрывать.
А с битовыми операциями другое.
Там a << b + c может по замыслу программиста быть и (a << b) + c и a << (b + c) поэтому с целью увеличения читаемости там скобки не помешают.
Всегда без лишних скобок. Они чаще только запутывают и ухудшают читаемость.
А если возникает необходимость в скобках для улучшения читаемости, то стоит рассмотреть вариант с изменением порядка условий (не нарушая, разумеется, логику) и разделением на несколько строк.
Sign up to leave a comment.

Articles