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


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);

Цей приклад показує, що якщо значення має бути змінено на другу половину виразу, це призведе до різниці між результатами, оскільки побітовий режим є прагнутим оператором, тоді як умовне поводиться як коротке замикання (не оцінює друге половина, якщо перша половина помилкова у випадку && та справжня у випадку ||).

У мене є проблема з цим прикладом. Чому ви хочете змінити значення одночасно з порівнянням на ньому? Це не здається надійним способом кодування. Я завжди був проти того, щоб робити кілька операцій в одному рядку в своєму виробничому коді. Здається, щось таке "кодує ковбой", не маючи сумління щодо ремонтопридатності свого коду. Я знаю, що в деяких областях код повинен бути максимально компактним, але, звичайно, це погана практика взагалі?

Я можу пояснити свій вибір заохочення до використання && та || над & і | тому що це прийнята норма кодування в інженерії програмного забезпечення .

Але чи міг би хтось надати мені кращий, навіть реальний, приклад використання побітового оператора в умовному виразі?

Відповіді:


16

це доцільно, коли ви виконуєте операцію маскування

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

де a і b цілі числа

|| та | і && і & не є взаємозамінними

| і & майже ніколи не з'являться в умовному виразі самі собою (точка посилання, яке ви включили, полягає в тому, що такі речі найчастіше є помилками)

EDIT: Не сперечайтеся зі своїм наставником; навіть якщо ви виграєте, ви програєте. Натомість поясніть, що ви не хочете плутати учнів, змішуючи логічні оператори та бітові оператори на одному уроці. Ви можете пояснити, що якщо i = 3 і j = 2, то i & j = 2, тоді як i& j - помилка. Однак, простіше пояснення полягає в тому, що ви навчаєте булевих (логічних) операторів, тому кидання в спеціальні регістри бітових еквівалентів - це відволікання від основної точки уроку. Немає необхідності робити наставника "неправильним", і не потрібно наводити зустрічні приклади. У центрі уваги уроку є булі оператори, а не побітові оператори.

Як наслідок, коли ви починаєте навчати розрядних операторів, немає необхідності показувати особливі випадки, коли + і - дають ті самі результати, що і & |


Код, наведений вище, не є помилковим, він може заплутати, але, як видно, код не містить помилки, чи не так? Я погоджуюся, що використання побітових операторів таким чином не дуже читабельно, мені не сподобається, якби я бачив це в огляді коду, але це законна Java (і C # теж, ігноруючи System.out.println).
Стів

Дякуємо за відповідь @Steven A. Lowe. Ви сказали, що "|| і | і && і & не є взаємозамінними", але bitwise = !(true & true == false);і condition = !(true && true == false);обидва оцінюватимуть як істинне, тож у цьому випадку вони взаємозамінні? Синтаксично можливо, оскільки код все ще компілюється. Я погодився б, що вони використовуються для різних речей семантично, як я вже згадував у пункті 2. Ви це кажете! і & "майже ніколи не з'являються в умовному собі". Я шукаю ці «майже ніколи» випадки і цікавлюсь, чи існують вони законно.
Дераша

@Deerasha: || і && працюють лише на булевих. & і | оперувати цілими числами та булевими - мало сенсу використовувати побітові оператори (призначені для роботи з декількома бітами ) для маніпулювання булевими елементами, і це робити при відмові може призвести до заплутаного коду та несподіваної поведінки (тобто якщо ви випадково використовуєте ціле число замість цілого булеві, розрядні оператори не будуть скаржитися)
Стівен А. Лоу

Так @Steve Haigh, компілятор не відкидає його, але це не правильний спосіб їх використання, судячи з опублікованого стандарту кодування, до якого я пов’язаний. Чи можу я зручно зупинитися на його відхиленні, оскільки він не відповідає стандарту кодування, чи ява може, вказуючи, що це неправильне використання?
Дераша

2
@Steven A. Lowe: якщо 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? У мене немає прикладу, коли два оператори не працюють аналогічно булевим операндам. Я погоджуюся з вашою думкою про більш читабельний і простіший в обслуговуванні код. Хочу заохотити їх писати прекрасний код. Але мати доказ у своїй сумці з інструментами було б переконливішим, ніж «тому що я це сказав». У мене це вказано як стандарт на цьому посиланні, і, можливо, доведеться покладатися тільки на це, якщо я не знайду приклад, який шукаю. Коли я запитав @Steve Haigh: чи повинна java вказувати на це як на неправильне використання?
Дераша

@Deerasha Я думав більше про суперечки з вашим наставником. Для класу навіть не намагайтеся сказати їм, що ви можете використовувати побітові оператори для логічних умов.
Кеті Ван Стоун

4

Ви будете використовувати бітові оператори, якщо порівнювати перелічення Bitmask. Наприклад, у вас є перелік станів і об'єкт, який може знаходитися в більш ніж одному з цих станів. У цьому випадку ви зробите побіжно або призначите більше одного стану своєму об’єкту.

наприклад, state = CONNECTED | IN_PROGRESSде CONNECTED could be 0x00000001іIN_PROGRESS 0x00000010

Для отримання додаткової інформації відшукайте документацію, яка містить прапор.


Завдяки вам я навчився застосуванню розрядних операторів, про яких раніше не знав! Але в цьому прикладі застосовується лише бітовий оператор. Я шукаю фрагмент добре написаного коду, де використання побітового, а не умовного способу призведе до компіляції, але неправильного виводу. Тобто, якщо такий фрагмент коду існує.
Дераша

Я не думаю, що це може статися на 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для нульових цілих чисел, правда?)
Хав'єр,

Nope @Javier: Неможливо передати з int до boolean.
Дераша

про що ((condA!=0) && (condB!=0))?
Хав'єр

@Javier yay це компілює, але "неправильність" більше не проілюстрована, if (((condA!=0) && (condB!=0))) { System.out.println("correct"); } if (((condA!=0) & (condB!=0))) { System.out.println("never executed?"); }виконує обидві заяви друку.
Дераша
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.