Практика проверки «правильности» или «равенства» в условных выражениях - имеет ли это смысл? - PullRequest
5 голосов
/ 10 апреля 2010

Я помню много лет назад, когда я учился в школе, один из моих учителей информатики учил нас, что лучше проверять «правильность» или «равенство» условия, а не негативные вещи, такие как «неравенство».

Позвольте мне уточнить - если часть условного кода можно написать, проверив, является ли выражение истинным или ложным, мы должны проверить «правильность».

Пример: выяснить, является ли число нечетным - это можно сделать двумя способами:

if ( num % 2 != 0 )
{
  // Number is odd
}

или

if ( num % 2 == 1 )
{
  // Number is odd
}

(Пожалуйста, обратитесь к помеченному ответу для лучшего примера.)

Когда я начинал кодировать, я знал, что num % 2 == 0 подразумевает, что число четное, поэтому я просто положил туда !, чтобы проверить, нечетно ли оно. Но он был как «Не проверять условия НЕ. По возможности проверяйте «правильность» или «равенство» условий. И он рекомендовал мне использовать второй кусок кода.

Я не за и не против, но я просто хотел знать - какая разница? Пожалуйста, не отвечайте «Технически, результат будет одинаковым» - мы ВСЕ знаем это. Это общая практика программирования или это его собственная практика программирования, которую он проповедует другим?

ПРИМЕЧАНИЕ: Я использовал синтаксис в стиле C # / C ++ без причины. Мой вопрос в равной степени применим при использовании операторов IsNot, <> в VB и т. Д. Так что удобочитаемость '!' Оператор - это только одна из проблем. Не проблема.

Ответы [ 8 ]

6 голосов
/ 10 апреля 2010

Проблема возникает, когда позже в проекте добавляется больше условий - один из проектов, над которыми я сейчас работаю, постоянно собирал условия (а затем некоторые из этих условий были перемещены в теги Struts, а затем некоторые в JSTL ...) - один минус не сложно прочитать, но 5+ - это кошмар, особенно когда кто-то решает реорганизовать и свести на нет все это. Возможно, в новом проекте вы напишите:

if (authorityLvl!=Admin){
 doA();
}else{
 doB();
}

Возвращайтесь через месяц, и это станет так:

if (!(authorityLvl!=Admin && authorityLvl!=Manager)){
 doB();
}else{
 doA();
}

Все еще довольно просто, но это занимает еще одну секунду.
Теперь дайте гнить еще 5-10 лет.
(x% 2! = 0), конечно, не проблема, но, возможно, лучший способ избежать описанного выше сценария - научить студентов не использовать негативные условия как общее правило, в надежде, что они будут использовать какое-то суждение раньше они делают - потому что просто сказать, что это может стать проблемой обслуживания, вероятно, будет недостаточно мотивации.

В качестве дополнения, лучший способ написать код:

userHasAuthority = (authorityLvl==Admin);
if (userHasAuthority){
 doB();
else{
 doA();
}

Теперь будущие кодеры с большей вероятностью просто добавят «|| authorityLvl == Manager», userHasAuthority легче перейти в метод, и даже если условное выражение реорганизовано, у него будет только один минус. Более того, никто не добавит дыру в безопасности приложения, допустив ошибку при применении закона Де Моргана.

2 голосов
/ 10 апреля 2010

Я не согласен с вашим старым профессором - проверка условия НЕ подходит, если вы проверяете определенное условие НЕ. Это на самом деле соответствует его критериям: вы будете проверять, что это ИСТИННО, что значение НЕ является чем-то.

Хотя я понимаю, что он имеет в виду - в основном истинные условия будут на несколько порядков меньше, чем условия NOT, поэтому их легче проверять, поскольку вы проверяете меньший набор значений.

1 голос
/ 10 апреля 2010

Вы все равно должны быть осторожны с такими вещами:

if ( num % 2 == 1 )
{
  // Number is odd
}

Если num отрицательное и нечетное, то в зависимости от языка или реализации num% 2 может быть равно -1. В этом примечании нет ничего плохого в проверке на ложность, если она упрощает хотя бы синтаксис проверки. Кроме того, использование! = Является для меня более понятным, чем просто! может сливаться с круглыми скобками.

Чтобы проверить только правильность, вам нужно сделать:

if ( num % 2 == 1 || num % 2 == -1 )
{
  // Number is odd
}

Это просто пример, очевидно. Дело в том, что если использование отрицания допускает меньшее количество проверок или делает синтаксис проверок понятным, то это, безусловно, правильный путь (как в приведенном выше примере). Привязка себя к проверке правильности не делает вашу условную текстуру более читабельной.

1 голос
/ 10 апреля 2010

Мне говорили, что это связано с тем, насколько "видим" символ пинга (!) При чтении с экрана.

Если кто-то обычно «читает ским» код - возможно, потому что он чувствует, что его обычная скорость чтения слишком низкая - тогда можно легко пропустить !, что дает ему критическое неправильное понимание кода.

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

Два очень хороших разработчика, с которыми я работал (и очень их уважаю), напишут == false вместо использования ! по аналогичным причинам.

Ключевой фактор в моей голове - это не то, что работает для вас (или меня!), А то, что работает для парня, который поддерживает код. Если код никогда не будет виден или поддержан кем-либо еще, следуйте своей личной прихоти; если код должен быть поддержан другими, лучше ориентироваться ближе к середине пути. Небольшой (банальный!) Компромисс с вашей стороны сейчас может спасти кого-то еще неделю отладки позже.

Обновление : При дальнейшем рассмотрении я бы предложил выделить условие, поскольку отдельная функция предиката дала бы еще большую удобство сопровождения:

if (isOdd(num))
{
    // Number is odd
}
0 голосов
/ 06 ноября 2012

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

Вот проблема. Сегодня вы слушаете его и превращаете свой код в:

// Print black stripe on odd numbers
int zebra(int num) {
  if (num % 2 == 1) {
    // Number is odd
    printf("*****\n");
  }
}

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

#define ZEBRA_PITCH 2
[snip pages and pages, these might even be in separate files - .h and .c]
// Print black stripe on non-multiples of ZEBRA_PITCH
int zebra(int num) {
  if (num % ZEBRA_PITCH == 1) {
    // Number is not a multiple of ZEBRA_PITCH
    printf("*****\n");
  }
}

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

Но вы еще не закончили. Вы хотите поддержать мутантов зебр, чьи черные полосы толще, чем их белые полосы. Из прошлых месяцев вы помните, что изначально кодировали его так, что ваш код печатает черную полосу там, где ее не должно быть - на четных числах. Так что все, что вам нужно сделать, это разделить, скажем, на 3, а не на 2, и все должно быть сделано. Правильно? Ну:

#define DEFAULT_ZEBRA_PITCH 2
[snip pages and pages, these might even be in separate files - .h and .c]
// Print black stripe on non-multiples of pitch
int zebra(int num, int pitch) {
  if (num % pitch == 1) {
    // Number is odd
    printf("*****\n");
  }
}

Эй, что это? Теперь у вас есть в основном - белые зебры, где вы ожидали, что они будут в основном черными!

Проблема здесь в том, как думать о числах. Является ли число «нечетным», потому что оно не четное или потому, что при делении на 2 остаток равен 1? Иногда ваш проблемный домен будет предлагать предпочтение одному, и в этих случаях я бы посоветовал вам написать свой код, чтобы выразить эту идиому, а не зацикливаться на упрощенных правилах, таких как «не проверять отрицания».

0 голосов
/ 05 февраля 2011

Отвечая Бевану (это не помещалось в комментарии):

Ты прав. !foo не всегда совпадает с foo == false. Давайте посмотрим на этот пример в JavaScript:

var foo = true,
    bar = false,
    baz = null;

foo == false; // false
!foo;         // false
bar == false; // true
!bar;         // true
baz == false; // false (!)
!baz;         // true
0 голосов
/ 10 апреля 2010

Действительно очень непоследовательная проблема. Однако один недостаток проверки в этом смысле заключается в том, что она работает только для двоичных сравнений. Например, если вы проверяете какое-либо свойство троичной числовой системы, вы будете ограничены.

0 голосов
/ 10 апреля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...