2013-08-21 12 views
6

Rozumiem różnicę poniżej (przynajmniej dla Java):Zwarcie vs podmiotów niebędących zwarciowych

if(true || false) // short-circuiting boolean operator 
if(true | false) // non-short-circuiting boolean operator 

Ale moje pytanie brzmi, istnieje jakikolwiek powód, aby użyć operatora niż krótkie spięcie, kiedy mają do czynienia z wyrażeń binarnych? Czy są jakieś korzyści związane z wydajnością lub używanie, które nie byłyby uważane za złe praktyki?

+1

Nie można przypadkowo użyć '|' jako operatora bitowego; oba operandy musiałyby być albo boolean albo integral. A w warunkowym wyrażeniu jesteś ograniczony do wartości logicznej. – cHao

+0

@CHao Yep, poprawne. Usunięto go tuż przed tym, jak zobaczyłem twój komentarz, ponieważ nie było to tak naprawdę związane z faktycznym pytaniem. – kin3tik

+0

możliwy duplikat [Przyczyna zaistnienia operatorów logicznych niezwiązanych z zwarciem] (http://stackoverflow.com/questions/9264897/reason-for-the-exsistance-of-non-short-circuit-logical-operators) – fredoverflow

Odpowiedz

9

Jednym z powodów, dla których możesz chcieć użyć operatora, który nie działa w zwarciu, jest sytuacja, w której w jakiś sposób zależy od skutków ubocznych funkcji. Na przykład.

boolean isBig(String text) { 
    System.out.println(text); 
    return text.length() > 10; 
} 

... 
if(isBig(string1) || isBig(string2)){ 
    ... 
} 

Jeśli nie dbam o to, czy println jest wykonywany wtedy trzeba zastosować krótkie operacje obwodu jak wyżej. Jeśli jednak chcesz, aby oba łańcuchy były drukowane zawsze (a więc w zależności od efektów ubocznych), musisz użyć operatora, który nie działa w zwarciu.

Praktycznie mówiąc, prawie zawsze chcesz używać operatorów zwarć. Opieranie się na efektach ubocznych w wyrażeniach jest zwykle złą praktyką programistyczną.

Jeden wyjątek dotyczy kodu bardzo niskiego poziomu lub kodu wrażliwego na wydajność. Operatory zwarcia mogą być nieco wolniejsze, ponieważ powodują rozgałęzienia podczas wykonywania programu. Również użycie operatorów bitowych umożliwia wykonywanie 32 lub 64 równoległych operacji logicznych jako jednej operacji na liczbach całkowitych, co jest bardzo szybkie.

+0

Ach, to jest dobry punkt, którego nie rozważałem. Ale czy poleganie na takich efektach nie byłoby ogólnie uważane za złą praktykę? – kin3tik

+0

Tak, dodałem ten punkt do odpowiedzi tuż przed tym, jak zobaczyłem twój komentarz! To rzeczywiście zła praktyka. –

+0

Po dodaniu zmian wydaje się, że to najlepsza odpowiedź na moje pytanie (obejmujące efekty uboczne rhs i sporadyczne korzyści wydajnościowe). – kin3tik

4

jedynym miejscem, gdzie nie powinno się używać non krótkie operatora obwód jest, gdy chcesz Druga instrukcja do wykonania, które nie powinny być przypadek w instrukcji warunkowych ogólnie

nr, nie ma wydajność z non zwarcie, ale nie ma absolutnie korzystać z zwarcie

2

Jeśli używasz,

if(true || /*someStatementHere*/) 

wtedy cały if blok będzie prawdą, ponieważ pierwszy urządz cja jest prawdą więc does'nt trzeba sprawdzić dla innych

prostu mówiąc, prawa ręka strona argumentu nie zostanie oceniona, jeśli pierwszy warunek daje wynik w przypadku operatorów zwarciowych

+0

Rozumiem różnicę, moje pytanie jest bardziej skoncentrowane na znalezieniu korzyści z używania '|', gdy tylko zajmujemy się wartościami binarnymi. – kin3tik

+0

Jedyną korzyścią, jaką można uzyskać, jest to, że operator zwarcia nie będzie oceniał stanu nieprawidłowego. –

+1

Pytanie, na które odpowiadasz, jest przeciwieństwem zadawanego pytania. Już wiemy, że '' '' zwarcia. Pytanie brzmi, czy istnieje jakiś dobry powód, by preferować '|' over' || '? – cHao

4

short-circuit, co oznacza, że nie oceniaj prawej strony, jeśli nie jest to konieczne. Jako przykład, jeśli po lewej stronie znajduje się &&, nie trzeba oceniać prawej strony. W innym przypadku ||, jeśli pozostało, nie ma potrzeby oceny prawej strony.

non-short oceniają zawsze obie strony.

Wtedy oczywiście jest korzyść z operatorów short-circuit.

A korzyści nie-krótkie, można znaleźć odpowiedź tutaj. Are there good uses for non-short-circuiting logical (boolean) operators in Java/Scala?

Rozważmy następujący przykład zbyt

while (status){ // status is boolean value 
      if(!a | result){// a and result are boolean value 
       result=getResult(); // result can change time to time 
      } 
     } 

musimy sprawdzić obie strony już teraz.

+0

Rozumiem różnicę, moje pytanie jest bardziej skoncentrowane na znalezieniu korzyści z używania '|', gdy tylko zajmujemy się wartościami binarnymi. – kin3tik

+0

@ kin3tik Dodałem więcej informacji do mojej odpowiedzi –

2

Trzecie oświadczenie

if(10 | 11)  // bitwise operator 

spowoduje błąd kompilacji. W instrukcji if można umieszczać tylko wartości logiczne.

Jeśli używasz IDE takiego jak Eclipse, automatycznie wyświetli drugie wyrażenie, to znaczy po || jako martwy kod dla operatora boolowskiego zwarcia.

+1

Masz rację. Usuwam tę linię z pytania, ponieważ nie jest to naprawdę istotne. – kin3tik

2

Dla prostych wyrażeń logicznych | jest czasami szybszy niż ||. I czasami chcesz wyrażenia być oceniane jednakowo (jak wtedy, gdy istnieje ++ w wartości indeksu.

Nie można przypadkowo „mix” Boolean | i bitwise | ponieważ nie można mieszać logicznych i całkowitych argumentów. Ale ty można (oczywiście) przypadkowo użyć | gdzie chesz użyć || Wystarczy być wdzięczny, że nie jest to C, gdzie można przypadkowo użyć = zamiast ==

+0

'{boolean b = false; if (b = true) {/ * do rzeczy * /}} 'jest faktycznie dozwolone i robi rzeczy. Który rodzaj bałaganu z twoim ostatnim zdaniem. :) – cHao

+0

@CHao - Tak, ale nie możesz powiedzieć 'if (intVar1 = intVar2)' i przenieść go poza kompilator w Javie. C jednak z przyjemnością to zaakceptuje. (I twój przykład jest jednym z powodów, dla których należy unikać wyrażeń takich jak 'boolValue == true (lub false)' vs po prostu 'boolValue' lub'! BoolValue'.) –

5

moje pytanie jest bardziej skoncentrowany na znalezieniu korzyści korzystania.. | gdy masz do czynienia tylko z booleanami:

Rozważmy następujący przypadek

while (!credentialvalid() | (loginAttempts++ < MAX)) { 

    // tell something to user. 

} 

w tym przypadku | potrzebne bo muszę zwiększyć liczyć próba zbyt :)

+0

To jest (bardzo) spóźniony komentarz, ale odradzam używając | w ten sposób. To nie jest intuicyjne i najlepiej, jeśli przyrost wartości LoginAttempts zostałby przeniesiony z warunkowego, jeśli zawsze musi być wykonany. –

6

Jeśli kod jest wystarczająco czuły wydajność i operacje na tyle tanie, używając brak zwarcia może być szybszy. Wynika to z faktu, że używanie || wymaga wykonywania odgałęzień, a brak przewidywania rozgałęzień może być bardzo kosztowny. Gdzie jako | wykonuje obliczenia i badanie zmiennej może być znacznie szybsze, unikając przewidywania rozgałęzienia miss.

Uwaga: jest to mikrooptymalizacja, która rzadko będzie widoczna, chyba że zostanie wywołana wiele, wiele razy.

+0

Teoretycznie JIT może zoptymalizować oba przypadki do drugiego, w którym RHS nie ma skutków ubocznych, więc w idealnym świecie wydajność nie byłaby brana pod uwagę (zawsze można użyć '||' dla efektu ubocznego bezpłatne porównania). Na przykład klasa "LongMath' firmy Guava * używa * tej optymalizacji do sprawdzania przepełnienia:' (a^b) <0 | (a^result)> = 0'. Nie sprawdziłem, czy Hotspot jest wystarczająco inteligentny, aby * faktycznie * skompilować ten sam kod dla obu przypadków. To tak, jak operator potrójny może oznaczać kod wolny od gałęzi versus if/else, ale dobry kompilator generuje ten sam kod w obu przypadkach. – BeeOnRope

+0

@BeeOnRope W wielu przypadkach JIT jest wystarczająco inteligentny, aby użyć instrukcji montażu * warunkowego ruchu *. na przykład oznacza to maksymalną/min 'a> b? a: wyrażenie b' nie potrzebuje gałęzi. –

+0

Rzeczywiście - moja uwaga dotyczyła raczej tego, jak optymalizacja czasami powoduje, że porady dotyczące wydajności są przestarzałe. W szczególności, w przypadku gcc bez optymalizacji, 'if/else' zawsze generuje gałęzie, a operator trójskładnikowy generuje' CMOV' (jeśli to możliwe, np. Wybory są prymitywami). Biorąc to pod uwagę, możesz zacząć polecać, że '?:' Jest używane, gdy gałęzie są nieprzewidywalne, ponieważ kompilator traktuje je inaczej. Jednak po włączeniu optymalizacji różnica znika: oba są kompilowane do kodu bez gałęzi. Podejrzewam, że występuje podobna równoważność dla '||' i '|'. – BeeOnRope