Kiedy właściwe jest użycie operatora bitowego w wyrażeniu warunkowym?

15

Po pierwsze, pewne tło: jestem nauczycielem informatyki i staram się wprowadzić operatorów logicznych języka java do mojej klasy 10. Mój nauczyciel-mentor przejrzał przygotowany przeze mnie arkusz roboczy i skomentował, że mogę pozwolić im użyć tylko jednego & lub | oznaczać operatorów, ponieważ „robią to samo”.

Zdaję sobie sprawę z różnicy między & i &&.
& jest operatorem bitowym przeznaczonym do stosowania między liczbami całkowitymi, do wykonywania „kruszenia bitów”.
&& to operator warunkowy przeznaczony do użycia między wartościami logicznymi.

Aby udowodnić, że operatorzy ci nie zawsze „robią to samo”, postanowiłem znaleźć przykład, w którym użycie bitowej między wartościami logicznymi doprowadziłoby do błędu. Znalazłem ten przykład

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

Ten przykład pokazuje, że jeśli wartość musi zostać zmieniona przez drugą połowę wyrażenia, doprowadziłoby to do różnicy między wynikami, ponieważ bitowy jest chętnym operatorem, podczas gdy warunek zachowuje się jak zwarcie (nie ocenia drugiej połowa, jeśli pierwsza połowa jest fałszywa w przypadku && i prawdziwa w przypadku ||).

Mam problem z tym przykładem. Dlaczego chcesz zmienić wartość w tym samym czasie, gdy porównujesz ją? Nie wydaje się to dobrym sposobem na kodowanie. Zawsze byłem niechętny wykonywaniu wielu operacji w jednym wierszu w moim kodzie produkcyjnym. Wygląda na to, że zrobiłby to „kodujący kowboj” bez sumienia co do możliwości utrzymania jego kodu. Wiem, że w niektórych domenach kod musi być tak zwarty, jak to możliwe, ale z pewnością jest to ogólnie słaba praktyka?

Mogę wyjaśnić mój wybór zachęcania do korzystania z && i || ponad i | ponieważ jest to przyjęta konwencja kodowania w inżynierii oprogramowania .

Ale czy ktoś mógłby mi podać lepszy, nawet rzeczywisty przykład użycia operatora bitowego w wyrażeniu warunkowym?

Deerasha
źródło

Odpowiedzi:

16

jest to właściwe podczas wykonywania operacji maskowania

jeśli ((a & b)> 0) {...}

gdzie aib są liczbami całkowitymi

|| i | oraz && i & nie są wymienne

| i amosty nigdy nie pojawią się same w wyrażeniu warunkowym (podany przez ciebie link jest taki, że takie rzeczy są najczęściej błędami)

EDYCJA: Nie kłóć się ze swoim mentorem; nawet jeśli wygrasz, przegrasz. Zamiast tego wyjaśnij, że nie chcesz mylić uczniów, łącząc operatory logiczne i operatory bitowe w tej samej lekcji. Można wyjaśnić, że jeśli i = 3 i j = 2, to i & j = 2, podczas gdy i&&j jest błędem. Jednak prostszym wyjaśnieniem jest to, że uczysz operatorów boolowskich (logicznych), więc rzucanie w specjalnych przypadkach bitowych ekwiwalentów odwraca uwagę od głównego punktu lekcji. Nie ma potrzeby, aby mentor był „zły” i nie trzeba przedstawiać kontrprzykładów. Lekcja skupia się na operatorach boolowskich, a nie operacjach bitowych.

W konsekwencji, kiedy zaczniesz uczyć operatorów bitowych, nie musisz pokazywać specjalnych przypadków, w których + i - dają takie same wyniki jak & i |

Steven A. Lowe
źródło
Powyższy kod nie jest jednak błędny, może być mylący, ale w tej sytuacji kod nie zawiera błędu, prawda? Zgadzam się, że używanie operatorów bitowych w ten sposób nie jest zbyt czytelne, nie spodobałoby mi się, gdybym zobaczył to w przeglądzie kodu, ale jest to legalna Java (i C #, ignorując System.out.println).
Steve
Dzięki za odpowiedź @Steven A. Lowe. Mówiłeś „|| i | i && i & nie są wymienne”, ale bitwise = !(true & true == false);i condition = !(true && true == false);będzie oceniać zarówno na true, a więc w tym przypadku są one wymienne? Być może składniowo, ponieważ kod nadal się kompiluje. Zgodziłbym się, że są one używane semantycznie do różnych rzeczy, jak wspomniałem w ust. 2. Mówicie to! i „prawie nigdy nie pojawiają się w stanie warunkowym”. Szukam tych „prawie nigdy” przypadków i zastanawiam się, czy istnieją one zgodnie z prawem.
Deerasha
@Deerasha: || i && działają tylko na boolach. i | operować na liczbach całkowitych i logicznych - nie ma sensu używać operatorów bitowych (przeznaczonych do działania na wielu bitach ) do manipulowania liczbami logicznymi, a wykonywanie tego porzucając może prowadzić do mylącego kodu i nieoczekiwanych zachowań (np. jeśli przypadkowo użyjesz liczby całkowitej zamiast boolean, operatorzy bitowi nie będą narzekać)
Steven A. Lowe
Tak @ Steve Haigh, kompilator go nie odrzuca, ale nie jest to właściwy sposób korzystania z nich, sądząc z opublikowanego standardu kodowania, z którym się łączyłem. Czy mogę wygodnie odpoczywać po jego odrzuceniu, ponieważ nie jest on zgodny ze standardem kodowania, czy też java może wskazywać, że jest to niewłaściwe użycie?
Deerasha
2
@Steven A. Lowe: jeśli i = 3 i j = 2, to i & j = 2, podczas gdy i && j jest błędem To jest genialne! To proste i ma sens. Na poziomie dziesiątym jest to również prawdopodobny błąd, ponieważ wciąż przyzwyczajają się do typu logicznego i tego, do czego operatorzy by się zastosowali. Dziękuję bardzo! Świetna rada, żeby nie kłócić się z moim mentorem.
Deerasha
12

Operatory niebędące bitami &&i ||są operatorami zwarć. Innymi słowy, &&jeśli, jeśli LHS jest fałszywe, RHS nigdy nie będzie oceniane; ze ||jeśli LHS jest prawdą, wówczas RHS nigdy nie będą oceniane. Z drugiej strony operatory bitowe &i |nie powodują zwarcia i zawsze będą oceniać zarówno LHS, jak i RHS. W przeciwnym razie są równoważne w ifinstrukcji.

Jedyną rzeczą, w której widzę wartość w stosowaniu operatorów bez zwarć, jest to, czy RHS ma jakiś pożądany efekt uboczny, który chcesz zdarzyć we wszystkich przypadkach. Nie mogę wymyślić konkretnego przykładu, w którym chciałbyś tego i nie sądzę, że to dobra praktyka, ale taka jest różnica.

Jonathan
źródło
1
+1. Dokładnie słusznie, porównując logiczne operacje logiczne i operatory logiczne zawsze zwracają ten sam wynik, ale (przynajmniej w Javie i C #) tylko operatory logiczne ulegają zwarciu.
Steve
Dziękuje za odpowiadanie. Twój akapit 1 był tym, do czego starałem się dojść w moim akapicie 4, stwierdzając, że bitowy jest chętnym operatorem, podczas gdy warunek zachowuje się jak zwarcie . Twój akapit 2 dotyczy problemu, który opisałem w moim akapicie 5. Więc zdaję sobie sprawę z różnicy, ale szukam tego konkretnego przykładu, o którym ani my, ani ja nie możemy myśleć.
Deerasha
7

Ogólna filozoficzna odpowiedź brzmi: użycie operatorów bitowych dla operatorów logicznych jest nietypowe i sprawia, że ​​kod jest trudniejszy do odczytania. W praktyce (w przypadku kodu, który jest produkowany), bardziej czytelny kod jest łatwiejszy w utrzymaniu, a zatem bardziej pożądany.

W przypadku rzeczywistego wykorzystania potrzeby operatorów zwarć zobacz przypadki takie jak:

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

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

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

Tego typu operacje często pojawiają się w kodzie rzeczywistym.

Kathy Van Stone
źródło
Tfa Jak wytłumaczyć moim 15-latkom, że 1 &jest „trudniejszy do odczytania” niż 2? Nie mam przykładu, w którym 2 operatory nie działają w taki sam sposób dla operandów boolowskich. Zgadzam się z twoją opinią na temat bardziej czytelnego i łatwiejszego do utrzymania kodu. Chcę ich zachęcić do napisania pięknego kodu. Ale posiadanie jakiegoś dowodu w mojej torbie narzędzi byłoby bardziej przekonujące niż „ponieważ tak powiedziałem”. Podaję to jako standard pod tym linkiem i być może będę musiał polegać na tym samym, jeśli nie otrzymam przykładu, którego szukam. Jak zapytałem @Steve Haigh: czy Java powinna wskazywać to jako niewłaściwe użycie?
Deerasha
@Deerasha Myślałem bardziej o kłótni z twoim mentorem. Dla klasy nawet nie próbuj mówić im, że możesz używać operatorów bitowych dla warunków logicznych.
Kathy Van Stone,
4

Użyjesz operatorów bitowych, jeśli porównasz wyliczenia maski bitowej. Na przykład masz wyliczenie stanów i obiekt, który może znajdować się w więcej niż jednym z tych stanów. W takim przypadku zrobisz bitowo lub przypiszesz więcej niż jeden stan do swojego obiektu.

na przykład state = CONNECTED | IN_PROGRESSgdzie CONNECTED could be 0x00000001iIN_PROGRESS 0x00000010

Aby uzyskać więcej informacji, zapoznaj się z dokumentacją wyliczania flagi.

pwny
źródło
Dzięki tobie nauczyłem się aplikacji operatorów bitowych, o których wcześniej nie wiedziałem! Ale w tym przykładzie obowiązuje tylko operator bitowy. Szukam kawałka dobrze napisanego kodu, w którym użycie bitowej zamiast warunkowej prowadziłoby do kompilacji, ale niepoprawnego wyniku. To znaczy, jeśli taki fragment kodu istnieje.
Deerasha
Nie sądzę, że może się tak zdarzyć w Javie, ponieważ wierzę, że nazywam to lub & na wartościach, które nie mogą być bitowe i które wykonałyby lub wykonałyby logiczne | lub &. Nie pamiętam, czy tak jest w Javie, ale wiem na pewno, że jest w C # MSDN: "Operatory binarne | są predefiniowane dla typów całkowych i bool. Dla typów całkowych, | oblicza bitowe OR operandów. operatory bool, | oblicza logiczne OR swoich operandów "
pwny
Po kilku dalszych badaniach odkryłem, że jedyna różnica między | i || a & i && dla operandów logicznych w Javie to zachowanie zwarciowe, więc opisany przez Ciebie scenariusz nie jest tak naprawdę możliwy.
pwny,
0

prostszy przykład zła:

int condA=1, condB=2;

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

tutaj masz dwa warunki, oba są niezerowe; ale bitowa &powoduje zero.

Javier
źródło
@Javier: Dziękuję za odpowiedź, ale jestem trochę zdezorientowany. Pracuję w Javie, a ten kod się nie kompiluje. (condA && condB)błędy, ponieważ && nie działa przez 2 ints, tylko 2 booleany. (condA & condB)podczas gdy poprawne, ocenia na int iw java nie możemy powiedzieć, if(int)więc również błędy. Ale jako pierwszy rozumiesz, czego szukam - dokładnie ten przykład zła .
Deerasha
dawno nie korzystałem z Javy ... spróbuj (Boolean(condA) && Boolean(condB)) (myślę, że Boolean(x)to truedla liczb całkowitych niezerowych, prawda?)
Javier
Nope @Javier: Nie można rzutować z int na boolean.
Deerasha
co ((condA!=0) && (condB!=0))?
Javier
@Javier yay kompiluje, ale „zła” nie jest już ilustrowana, powoduje if (((condA!=0) && (condB!=0))) { System.out.println("correct"); } if (((condA!=0) & (condB!=0))) { System.out.println("never executed?"); }wykonanie obu instrukcji print.
Deerasha