Quelles sont les règles pour l’ordre d’évaluation en Java?

Je lis du texte Java et j’ai le code suivant:

int[] a = {4,4}; int b = 1; a[b] = b = 0; 

Dans le texte, l’auteur n’a pas donné d’explication claire et l’effet de la dernière ligne est: a[1] = 0;

Je ne suis pas sûr de comprendre: comment l’évaluation at-elle eu lieu?

Permettez-moi de dire ceci très clairement, car les gens comprennent mal cela tout le temps:

L’ordre d’évaluation des sous-expressions est indépendant de l’associativité et de la priorité . L’associativité et la priorité déterminent dans quel ordre les opérateurs sont exécutés mais ne déterminent pas dans quel ordre les sousexpressions sont évaluées. Votre question concerne l’ordre dans lequel les sousexpressions sont évaluées.

Considérez A() + B() + C() * D() . La multiplication est une priorité supérieure à l’addition, et l’addition est associative à gauche, ce qui équivaut à (A() + B()) + (C() * D()) mais sachant que cela ne fait que commencer avant la deuxième addition, et que la multiplication se produira avant la deuxième addition. Il ne vous dit pas dans quel ordre A (), B (), C () et D () seront appelés! (Il ne vous dit pas non plus si la multiplication se produit avant ou après le premier ajout.) Il serait parfaitement possible d’obéir aux règles de préséance et d’associativité en les compilant comme suit:

 d = D() // these four computations can happen in any order b = B() c = C() a = A() sum = a + b // these two computations can happen in any order product = c * d result = sum + product // this has to happen last 

Toutes les règles de préséance et d’associativité y sont suivies – la première addition se produit avant la deuxième addition et la multiplication se produit avant la seconde addition. Clairement, nous pouvons faire les appels à A (), B (), C () et D () dans n’importe quel ordre et toujours respecter les règles de préséance et d’associativité!

Nous avons besoin d’une règle sans rapport avec les règles de préséance et d’associativité pour expliquer l’ordre dans lequel les sous-expressions sont évaluées. La règle pertinente en Java (et C #) est que “les sous-expressions sont évaluées de gauche à droite”. Puisque A () apparaît à gauche de C (), A () est évalué en premier, indépendamment du fait que C () soit impliqué dans une multiplication et que A () ne soit impliqué que dans un ajout.

Alors maintenant, vous avez suffisamment d’informations pour répondre à votre question. Dans a[b] = b = 0 les règles d’associativité disent que c’est a[b] = (b = 0); mais cela ne signifie pas que le b=0 court en premier! Les règles de priorité indiquent que l’indexation est prioritaire par rapport à l’affectation, mais cela ne signifie pas que l’indexeur est exécuté avant l’affectation la plus à droite .

Les règles de préséance et d’associativité imposent les ressortingctions qui:

  • L’opération d’indexeur doit être exécutée avant l’opération associée à l’opération d’affectation à gauche
  • L’opération associée à l’opération d’affectation de droite doit s’exécuter avant celle associée à l’opération d’affectation de gauche.

La préséance et l’associativité ne nous disent que l’affectation de zéro à b doit avoir lieu avant l’affectation à a[b] . La préséance et l’associativité ne permettent pas de savoir si a[b] est évalué avant ou après le b=0 .

Encore une fois, ceci est identique à: A()[B()] = C() – Tout ce que nous soaps, c’est que l’indexation doit avoir lieu avant l’affectation. Nous ne soaps pas si A (), B () ou C () s’exécute en premier sur la priorité et l’associativité . Nous avons besoin d’une autre règle pour nous le dire.

La règle est, encore une fois, “lorsque vous avez le choix de ce que vous devez faire en premier, allez toujours de gauche à droite”: a[b] trouve à gauche de b=0 , donc a[b] s’exécute en premier . dans a[1] . Ensuite, le b=0 se produit, puis l’atsortingbution de la valeur à a[1] se produit en dernier.

Les choses à gauche se passent avant les choses à droite. C’est la règle que vous recherchez. Parler de préséance et d’associativité est à la fois déroutant et non pertinent.

Les gens se trompent tout le temps , même ceux qui devraient savoir mieux. J’ai édité beaucoup trop de livres de programmation qui énonçaient les règles de manière incorrecte, il n’est donc pas surprenant que de nombreuses personnes aient des croyances totalement erronées sur la relation entre préséance / associativité et ordre d’évaluation, à savoir qu’en réalité ; ils sont indépendants.

Si ce sujet vous intéresse, consultez mes articles sur le sujet pour en savoir plus:

http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/

Ils concernent C #, mais la plupart de ces choses s’appliquent également à Java.

La réponse magistrale d’Eric Lippert n’est néanmoins pas utile car elle parle d’une langue différente. Ceci est Java, où la spécification du langage Java est la description définitive de la sémantique. En particulier, le §15.26.1 est pertinent car il décrit l’ordre d’évaluation de l’opérateur = (nous soaps tous qu’il est associatif, oui?). En réduisant un peu les éléments qui nous intéressent dans cette question:

Si l’expression d’opérande de gauche est une expression d’access au tableau ( §15.13 ), plusieurs étapes sont nécessaires:

  • Tout d’abord, la sous-expression de référence de tableau de l’expression d’access au tableau d’opérandes de gauche est évaluée. Si cette évaluation se termine brusquement, l’expression d’affectation se termine brusquement pour la même raison; la sous-expression d’index (de l’expression d’access au tableau d’opérandes de gauche) et l’opérande de droite ne sont pas évalués et aucune affectation n’a lieu.
  • Sinon, la sous-expression d’index de l’expression d’access au tableau d’opérandes de gauche est évaluée. Si cette évaluation se termine brusquement, l’expression d’affectation se termine brusquement pour la même raison et l’opérande de droite n’est pas évalué et aucune affectation ne se produit.
  • Sinon, l’opérande de droite est évalué. Si cette évaluation se termine brusquement, l’expression d’affectation se termine brusquement pour la même raison et aucune affectation ne se produit.

[… Il décrit ensuite le sens réel de la mission elle-même, que nous pouvons ignorer ici pour des raisons de concision…]

En bref, Java a un ordre d’évaluation très étroitement défini , qui est à peu près exactement de gauche à droite dans les arguments de tout opérateur ou appel de méthode. Les affectations de tableaux sont l’un des cas les plus complexes, mais même là, il s’agit toujours de L2R. (Le JLS recommande de ne pas écrire de code qui nécessite ce type de contraintes sémantiques complexes , et moi aussi: vous pouvez avoir plus de problèmes que nécessaire avec une seule tâche par instruction!)

C et C ++ sont nettement différents de Java dans ce domaine: leurs définitions de langage laissent délibérément l’ordre d’évaluation indéfini pour permettre plus d’optimisations. C # est comme Java apparemment, mais je ne connais pas assez sa littérature pour pouvoir désigner la définition formelle. (Cela varie vraiment selon la langue, Ruby est ssortingctement L2R, de même que Tcl – bien qu’il n’y ait pas d’opérateur d’affectation en soi pour des raisons non pertinentes ici – et Python est L2R mais R2L en ce qui concerne l’affectation , .)

 a[b] = b = 0; 

1) l’opérateur d’indexation de tableau a une priorité plus élevée que l’opérateur d’affectation (voir cette réponse ):

 (a[b]) = b = 0; 

2) Selon 15.26. Opérateurs d’affectation de JLS

Il y a 12 opérateurs d’affectation; tous sont syntaxiquement associatifs à droite (ils se regroupent de droite à gauche). Ainsi, a = b = c signifie a = (b = c), qui assigne la valeur de c à b et assigne ensuite la valeur de b à a.

 (a[b]) = (b=0); 

3) Selon 15.7. Ordre d’évaluation de JLS

Le langage de programmation Java garantit que les opérandes des opérateurs semblent être évalués dans un ordre d’évaluation spécifique, à savoir de gauche à droite.

et

L’opérande de gauche d’un opérateur binary semble être complètement évalué avant qu’une partie quelconque de l’opérande de droite ne soit évaluée.

Alors:

a) (a[b]) évalué en premier sur a[1]

b) alors (b=0) évalué à 0

c) (a[1] = 0) évalué en dernier

Votre code est équivalent à:

 int[] a = {4,4}; int b = 1; c = b; b = 0; a[c] = b; 

ce qui explique le résultat.

Prenons un autre exemple plus approfondi ci-dessous.

En règle générale:

Il est préférable de disposer d’un tableau des règles et associativité de l’ordre de priorité lors de la résolution de ces questions, par exemple http://introcs.cs.princeton.edu/java/11precedence/

Voici un bon exemple:

 System.out.println(3+100/10*2-13); 

Question: Quelle est la sortie de la ligne ci-dessus?

Réponse: appliquer les règles de préséance et d’associativité

Etape 1: Selon les règles de préséance: / et * les opérateurs ont priorité sur les opérateurs + -. Par conséquent, le sharepoint départ pour exécuter cette équation sera réduit à:

 100/10*2 

Etape 2: Selon les règles et la préséance: / et * sont égaux en priorité.

Comme les opérateurs / et * ont la même priorité, nous devons examiner l’associativité entre ces opérateurs.

Selon les règles d’association de ces deux opérateurs particuliers, nous commençons à exécuter l’équation de gauche à droite, c’est-à-dire que 100/10 est exécuté en premier:

 100/10*2 =100/10 =10*2 =20 

Étape 3: L’équation est maintenant dans l’état d’exécution suivant:

 =3+20-13 

Selon les règles et la préséance: + et – sont égaux en priorité.

Nous devons maintenant examiner l’associativité entre les opérateurs + et – opérateurs. Selon l’associativité de ces deux opérateurs particuliers, on commence à exécuter l’équation de gauche à droite, c’est-à-dire que 3 + 20 s’exécute d’abord:

 =3+20 =23 =23-13 =10 

10 est la sortie correcte lorsqu’elle est compilée

Encore une fois, il est important d’avoir un tableau des règles de l’ordre de priorité et de l’associativité avec vous pour résoudre ces questions, par exemple http://introcs.cs.princeton.edu/java/11precedence/

 public class TestClass{   public static void main(Ssortingng args[] ){      int i = 0 ;      int[] iA = {10, 20} ;      iA[i] = i = 30 ; System.out.println(""+ iA[ 0 ] + " " + iA[ 1 ] + "  "+i) ;    } } 

Il imprimera 30 20 30

La déclaration iA [i] = i = 30; sera traité comme suit:

iA [i] = i = 30; => iA [0] = i = 30; => i = 30; iA [0] = i; => iA [0] = 30;

Voici ce que dit JLS à ce sujet:

1 Évaluer l’opérande de la main gauche en premier
2 Évaluer les opérandes avant l’opération
3 L’évaluation respecte les parenthèses et la préséance
4 listes d’arguments sont évaluées de gauche à droite

Pour les tableaux: Tout d’abord, les expressions de dimension sont évaluées, de gauche à droite. Si l’une des évaluations d’expression se termine brusquement, les expressions à sa droite ne sont pas évaluées.