Когда целесообразно использовать побитовый оператор в условном выражении?

15

Во-первых, немного предыстории: я учитель-информатик, и я пытаюсь представить булевы операторы Java в своем классе 10-го класса. Мой учитель-наставник просмотрел рабочий лист, который я подготовил, и прокомментировал, что я могу позволить им использовать только один & или | обозначать операторов, потому что они «делают то же самое».

Я осознаю разницу между & и &&.
& является побитовым оператором, предназначенным для использования между целыми числами, для выполнения "сдвоенного бита".
&& - это условный оператор, предназначенный для использования между логическими значениями.

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

boolean bitwise;
boolean conditional;
int i=10, j=12;
bitwise = (i<j) | ((i=3) > 5); // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i<j) || (i=3) > 5 ;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);
i=10; 
bitwise = (i>j) & (i=3) > 5;   // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i>j) && (i=3) > 5;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);

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

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

Я могу объяснить свой выбор поощрения использования && и || над & и | потому что это общепринятая кодировка в программной инженерии .

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

Deerasha
источник

Ответы:

16

это уместно, когда вы выполняете операцию маскировки

if ((a & b)> 0) {...}

где а и b - целые числа

|| и | и && и & не являются взаимозаменяемыми

| и & amost никогда не появятся в условном выражении сами по себе (смысл ссылки, которую вы включили, состоит в том, что такие вещи чаще всего являются ошибками)

РЕДАКТИРОВАТЬ: не спорьте с вашим наставником; даже если вы выиграете, вы проиграете. Вместо этого объясните, что вы не хотите путать студентов, смешивая логические операторы и побитовые операторы в одном и том же уроке. Вы можете объяснить, что если i = 3 и j = 2, то i & j = 2, а i && j - ошибка. Однако более простое объяснение состоит в том, что вы учите логические (логические) операторы, поэтому добавление побитовых эквивалентов в особом случае является отвлечением от основной темы урока. Нет необходимости делать наставника «неправильным» и не нужно приводить контрпримеры. Основное внимание в уроке уделяется логическим операторам, а не побитовым операторам.

Как следствие, когда вы начинаете обучать побитовые операторы, нет необходимости показывать особые случаи, когда + и - дают те же результаты, что и & и |

Стивен А. Лоу
источник
Приведенный выше код не является неправильным, хотя, это может сбивать с толку, но в том смысле, что код не содержит ошибки, не так ли? Я согласен, что использование побитовых операторов таким способом не очень читабельно, мне бы не понравилось, если бы я увидел его в обзоре кода, но это легальная Java (и C # тоже, игнорируя System.out.println).
Стив
Спасибо за ответ @ Стивен А. Лоу. Вы сказали , что «|| и | и && и & не являются взаимозаменяемыми» , но bitwise = !(true & true == false);и condition = !(true && true == false);будет как оценить, правда, так что в этом случае они взаимозаменяемы? Возможно, синтаксически, поскольку код все еще компилируется. Я бы согласился, что они используются для разных вещей семантически, как я упоминал в параграфе 2. Вы говорите это! и & "почти никогда не появляются в условных выражениях". Я ищу эти «почти никогда» дела и задаюсь вопросом, существуют ли они на законных основаниях.
Дираша
@Deerasha: || и && работают только с логическими значениями. & и | работать с целыми числами и логическими значениями - нет смысла использовать побитовые операторы (предназначенные для работы с несколькими битами ) для манипулирования логическими значениями, и это может привести к путанице в коде и неожиданному поведению (то есть, если вы случайно используете целое число вместо логическое, побитовые операторы не будут жаловаться)
Стивен А. Лоу
Да @ Steve Haigh, компилятор не отвергает его, но это неправильный способ их использования, судя по опубликованному стандарту кодирования, на который я ссылался. Могу ли я с комфортом отдохнуть, отклонив его, потому что он не соответствует стандарту кодирования, или ява может указать, что это неправильное использование?
Дираша
2
@ Стивен А. Лоу: если i = 3 и j = 2, то i & j = 2, а i && j - ошибка. Это замечательно! Это просто и это важно. В 10-м классе это также вероятная ошибка, так как они все еще привыкли к булевому типу и тому, к чему будут обращаться операторы. Спасибо огромное! Хороший совет не спорить с моим наставником.
Дираша
12

Небитовые операторы &&и ||являются операторами короткого замыкания. Другими словами, &&если LHS ложно, RHS никогда не будет оцениваться; с, ||если LHS истинно, тогда RHS никогда не будет оцениваться. С другой стороны, побитовые операторы &и не |являются короткозамкнутыми, и всегда будут оценивать как LHS, так и RHS. В противном случае они эквивалентны в ifутверждении.

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

Джонатан
источник
1
+1. Совершенно верно, при сравнении логических битовых и логических операторов всегда возвращают один и тот же результат, но (по крайней мере в Java и C #) только короткое замыкание логических операторов.
Стив
Спасибо, что ответили. Ваш параграф 1 был тем, к чему я стремился в моем параграфе 4, заявив, что побитовый оператор является нетерпеливым, а условное поведение ведет себя как короткое замыкание . Ваш параграф 2 - это проблема, которую я описал в параграфе 5. Итак, я знаю о разнице, но я ищу этот конкретный пример, который ни вы, ни я не можем придумать.
Дираша
7

Общий философский ответ заключается в том, что использование побитовых операторов для логических операторов нетипично и затрудняет чтение кода. На практике (для кода, который находится в производстве), более читаемый код легче поддерживать и, следовательно, более желателен.

Для реального использования потребности в операторах короткого замыкания, смотрите случаи, такие как:

if (args.length > 0 && args[0] == 'test') ....

if (b != NULL && b.some_function()) ...

if (b == NULL || b.some_function()) ...

Эти типы операций часто появляются в реальном коде.

Кэти Ван Стоун
источник
ТФ. Как мне объяснить моим 15-летним детям, что 1 &«труднее читать», чем 2? У меня нет примера, в котором 2 оператора не работают одинаково для булевых операндов. Я согласен с вашей точкой зрения по поводу более удобочитаемого и простого в обслуживании кода. Я хочу призвать их написать красивый код. Но иметь какое-то доказательство в сумке с инструментами было бы более убедительно, чем «потому что я так сказал». У меня есть ссылка на стандарт по этой ссылке, и, возможно, мне придется полагаться только на нее, если я не получу нужный мне пример. Как я спросил @Steve Haigh: должен ли Java указывать на это как ненадлежащее использование?
Дираша
@Deerasha Я больше думал о споре с твоим наставником. Для класса даже не пытайтесь сказать им, что вы можете использовать побитовые операторы для логических условий.
Кэти Ван Стоун
4

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

например, state = CONNECTED | IN_PROGRESSгде CONNECTED could be 0x00000001иIN_PROGRESS 0x00000010

Для получения дополнительной информации посмотрите документацию перечисления флагов.

pwny
источник
Благодаря вам я изучил применение побитовых операторов, о которых раньше не знал! Но в этом примере применяется только побитовый оператор. Я ищу кусок хорошо написанного кода, где использование побитового, а не условного приведет к компиляции, но неверный вывод. То есть, если такой фрагмент кода существует.
Дираша
Я не думаю, что это может произойти в Java, поскольку я считаю, что вызов | или & операторы со значениями, которые не могут быть побитовыми, или, или, или просто выполняет логическое | или &. Я не могу вспомнить, так ли это в Java, но точно знаю, что это в C # MSDN: «Двоичные | операторы предопределены для целочисленных типов и bool. Для целочисленных типов | вычисляет побитовое ИЛИ его операндов. bool операнды, | вычисляет логическое ИЛИ своих операндов "
pwny
После еще одного исследования я обнаружил, что единственная разница между | и || и & и && для булевых операндов в Java - это поведение короткого замыкания, поэтому описываемый вами сценарий на самом деле невозможен.
pwny
0

более простой пример неправильности:

int condA=1, condB=2;

if (condA!=0 && condB!=0) {
    // correct!
}
if ((condA & condB)!=0) {
    // never executed
}

здесь у вас есть два условия, оба ненулевые; но побитовое &приводит к нулю.

Хавьер
источник
@Javier: Спасибо за ответ, но я немного растерялся. Я работаю в Java, и этот код не компилируется. (condA && condB)ошибки, потому что && не работает в течение 2 intс, только 2 логических значений. (condA & condB)в то время как правильно, вычисляет до int и в Java, мы не можем сказать, if(int)что это тоже ошибки. Ты первый, кто понял, что я ищу, - именно этот пример неправильности .
Дираша
давно не пользовался Java ... попробуй (Boolean(condA) && Boolean(condB)) (думаю Boolean(x), trueдля ненулевых целых чисел, верно?)
Хавьер
Нет @Javier: Невозможно привести от int к логическому.
Дираша
о чем ((condA!=0) && (condB!=0))?
Хавьер
@Javier, да, это компилируется, но «неправильность» больше не проиллюстрирована, if (((condA!=0) && (condB!=0))) { System.out.println("correct"); } if (((condA!=0) & (condB!=0))) { System.out.println("never executed?"); }выполняет оба оператора печати.
Дираша