2010-01-26 4 views
20

Malinteso La precedenza degli operatori Java è una fonte di domande frequenti e errori impercettibili. Sono rimasto incuriosito dall'apprendere che anche lo Java Language Specification dice: "Si raccomanda che il codice non si basi in modo cruciale su questa specifica". JLS §15.7 Preferendo chiaro a intelligente, ci sono delle linee guida utili in questo settore?Linee guida per la precedenza degli operatori Java

Qui ci sono un certo numero di risorse sul tema:

aggiunte o correzioni benvenuto.

+5

In caso di dubbio, aggiungere più parentesi. –

+6

E quando ci sono troppe parentesi, refactoring. – trashgod

+4

Quella citazione JLS (nel contesto) incoraggia la decomposizione di affermazioni che hanno più effetti collaterali (o effetti collaterali nelle sottoespressioni) per evitare l'incertezza sul tempo relativo di detti effetti collaterali nelle sotto-espressioni destra e sinistra. Questo non può essere ottenuto aggiungendo parentesi. In altre parole, il JLS sconsiglia di fare affidamento sull'ordine di valutazione * di Java *, non contro il fatto di basarsi sulla precedenza degli operatori. La differenza tra questi è spiegata [sotto] (http://stackoverflow.com/a/13353263/13353263) e [qui] (http://stackoverflow.com/a/6801431/230513). – Museful

risposta

19

Per quanto riguarda il "mondo reale", probabilmente è giusto dire:

  • abbastanza programmatori sanno che la moltiplicazione/divisione hanno la precedenza su addizione/sottrazione, come è matematicamente la convenzione
  • difficilmente eventuali programmatori possono ricordare una qualsiasi delle altre regole di precedenza

Quindi, a parte il caso specifico di */ vs +-, mi piacerebbe davvero basta usare parentesi per definire esplicitamente la preceden ce inteso.

+4

Intendevi parentesi o parentesi? " –

+3

Il motivo per cui i programmatori difficilmente possono ricordare è perché ogni lingua è leggermente diversa. –

+0

Loro * sono * leggermente diversi in diverse lingue, ma anche i programmatori di una singola lingua generalmente non memorizzano la lista. @Anthony - nel mio mondo le parentesi sono un tipo di parentesi –

5

Un'altra fonte correlata di bug è come si accumulano gli errori di arrotondamento. Non un problema di ordine di precedenza dell'operatore di per sé, ma una fonte di sorpresa quando si ottiene un risultato diverso dopo la riorganizzazione degli operandi in modo aritmetico-equivalente. Ecco una versione sun.com di David Goldberg What Every Computer Scientist Should Know About Floating-Point Arithmetic.

1

Mi sembra che la maggior parte dei programmatori pensi che "la maggior parte degli altri programmatori" non sappia o non ricordi la precedenza degli operatori, quindi si concedono ciò che viene chiamato indulgentemente "programmazione difensiva" di 'inserendo le parentesi mancanti', giusto per 'chiarire' quello. Il fatto di ricordare questa roba di terza scelta è un vero problema è un'altra domanda. È altrettanto discutibile che tutto ciò è una completa perdita di tempo e se qualcosa peggiora le cose. La mia opinione è che la sintassi ridondante debba essere evitata laddove possibile, e che i programmatori di computer dovrebbero conoscere la lingua in cui stanno programmando, e forse anche aumentare le loro aspettative nei confronti dei loro colleghi.

+5

(+ (1)) a quello. – Museful

+0

Scusate, per me questo è polemico. Sono d'accordo di più con il JLS 15.7. Cambieranno mai l'ordine? Chissà. Ma posso dirvi che ho lavorato su progetti con codice C++ e Java, che hanno sottili differenze di precedenza (ad esempio: definito rispetto a una precedenza indefinita), e la maggior parte dei nostri sviluppatori preferirebbe essere certo dell'ordine di valutazione quando potrebbe essere la codifica di Java un giorno e C++ il prossimo. –

+0

@ OgrePsalm33 (a) Naturalmente è "argomentativo": se si intende "discutibile", la domanda ha invitato un dibattito; (b) JLS 15.7 non riguarda la precedenza degli operatori, ma l'ordine di valutazione; (c) no, non cambieranno mai la precedenza degli operatori; (d) non esiste una precedenza dell'operatore non definita in Java o C++ o in qualsiasi altra lingua di cui sono a conoscenza: è impossibile costruire un parser con una grammatica indefinita; (e) le parentesi non controllano l'ordine di valutazione. – EJP

4

La citazione (dallo Java Language Specification §15.7) deve essere letta nel contesto di Evaluation Order. Come discusso here, quella sezione riguarda l'ordine di valutazione , che non è correlato all'operatore precedence (o associatività).

precedenza e influenza associatività la struttura dell'albero espressione (cioè quale operatori agiscono su cui operandi), mentre "ordine di valutazione" influenza unicamente l'ordine in cui l'albero di espressione è attraversato quando la espressione è valutata. L'ordine di valutazione (o "ordine trasversale") non ha alcun effetto a meno che alcune sotto-espressioni abbiano effetti collaterali che influenzano il risultato (o gli effetti collaterali) di altre sottoespressioni.

Ad esempio, se x == 1 inizialmente, l'espressione ++x/++x sarebbe valutare come 2/3 (che restituisce 0) poiché Java è da sinistra a destra ordine di valutazione. Se l'ordine di valutazione in Java fosse stato da destra a sinistra, x sarebbe stato incrementato due volte prima della valutazione del numeratore e l'espressione sarebbe stata valutata come 3/2 (che valuta 1). Se l'ordine di valutazione fosse stato indefinito, l'espressione avrebbe potuto valutare uno di questi risultati.

La citazione in questione, insieme al suo contesto, ...

Java garantisce linguaggio di programmazione che gli operandi di operatori sembrano essere valutata in uno specifico ordine di valutazione, vale a dire, da sinistra a destra.

Si consiglia di non basare il codice su queste specifiche. codice è solitamente più chiaro quando ogni espressione contiene al massimo un effetto collaterale , come il suo funzionamento più esterno

... scoraggia il lettore dalla seconda sinistra a giustezza dell'ordine valutazione di Java (come in l'esempio sopra). Non incoraggia le parentesi inutili.

Modifica: risorsa: Java operator precedence table che funge anche da indice in sezioni del JLS contenenti la grammatica sintattica da cui viene dedotto ciascun livello di precedenza.

+1

+1 per il contesto. – trashgod

+0

L'ordine di valutazione si riferisce all'ordine in cui vengono valutate le sub espressioni, ad esempio (a + b)/(c + d). È garantito che sia lasciato prima di destra. Non influenza il modo in cui l'operatore stesso viene valutato minimamente. Il tuo esempio di 2/3 rispetto a 3/2 è completamente errato. Non ha nulla a che fare con tree traversal: è una proprietà del codice generato, dove non c'è affatto un albero delle espressioni, solo RPN. – EJP

+1

@EJP Ebbene RPN è solo un modo di codifica di un [albero di espressione] (http://en.wikipedia.org/wiki/Binary_expression_tree), e l'elaborazione sequenziale RPN pari esattamente alla attraversamento che mi riferivo.Per quanto riguarda la tua affermazione che il mio esempio è "completamente errato", affermi infatti che ++ x/++ x valuterà lo stesso risultato in ordine di valutazione LTR e RTL? – Museful

2

Inoltre, non dimenticare il logico & & e || sono gli operatori di scelta rapida, evitare qualcosa di simile:

sideeffect1() || sideeffect2() 

Se sideeffect1() sta valutando a true, sideeffect2() non sta per essere eseguito. Lo stesso vale per & & e falso. Questo non è del tutto corrispondenti a precendenza, ma in questi casi estremi il assiociativity può anche essere un aspetto importante che normalmente è davvero irrilevante (almeno per quanto mi riguarda)

+1

+1 anche se mi ritiro per l'orrore. :-) – trashgod

+0

In Java, gli operatori '&' e '|' possono essere logici booleani (così come interi bit a bit). Questi sono gli operatori non cortocircuitanti. Usare '||' invece di '|' è una richiesta esplicita per la valutazione dell'operando condizionale, molto simile a '?:'. – Museful

2

Il JLS non dà un operatore esplicito tabella delle precedenze; è implicito quando lo JLS descrive vari operatori. Ad esempio, la grammatica per ShiftExpression è questo:

ShiftExpression: 
    AdditiveExpression 
    ShiftExpression << AdditiveExpression 
    ShiftExpression >> AdditiveExpression 
    ShiftExpression >>> AdditiveExpression 

Ciò significa che operatori additivi (+ e -) hanno una priorità più alta rispetto agli operatori spostamento sinistra-associativo (<<, >> e >>>).