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 s'est-elle déroulée?
java
operator-precedence
ipkiss
la source
la source
Réponses:
Permettez-moi de le dire très clairement, car les gens comprennent mal cela tout le temps:
L'ordre d'évaluation des sous-expressions est indépendant à la fois de l'associativité et de la préséance . 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 sous - expressions sont évaluées. Votre question porte sur l'ordre dans lequel les sous - expressions sont évaluées.
Considérez
A() + B() + C() * D()
. La multiplication a une priorité plus élevée que l'addition, et l'addition est associative à gauche, donc cela équivaut à(A() + B()) + (C() * D())
Mais sachant que cela vous indique seulement que la première addition se produira 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 la première addition.) Il serait parfaitement possible d'obéir aux règles de préséance et d'associativité en compilant ceci comme: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 deuxième addition. Clairement, nous pouvons faire les appels à A (), B (), C () et D () dans n'importe quel ordre tout en obéissant aux 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 "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 () est impliqué dans une multiplication et A () n'est impliqué que dans une addition.
Alors maintenant, vous avez suffisamment d'informations pour répondre à votre question. Dans
a[b] = b = 0
les règles d'associativité disons que c'est,a[b] = (b = 0);
mais cela ne veut pas dire que lesb=0
courses en premier! Les règles de priorité indiquent que l'indexation a une priorité plus élevée que l'affectation, mais cela ne signifie pas que l'indexeur s'exécute avant l'affectation la plus à droite .(MISE À JOUR: Une version précédente de cette réponse avait quelques petites omissions pratiquement sans importance dans la section qui suit que j'ai corrigée. J'ai également écrit un article de blog décrivant pourquoi ces règles sont sensées en Java et C # ici: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )
La préséance et l'associativité nous indiquent seulement que l'affectation de zéro à
b
doit se produire avant l'affectation àa[b]
, car l'affectation de zéro calcule la valeur qui est affectée dans l'opération d'indexation. La préséance et l'associativité seules ne disent rien sur le fait de savoir si lea[b]
est évalué avant ou après leb=0
.Encore une fois, c'est exactement la même chose que:
A()[B()] = C()
- Tout ce que nous savons, c'est que l'indexation doit avoir lieu avant l'affectation. Nous ne savons pas si A (), B () ou C () s'exécute en premier en fonction de la priorité et de 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". Cependant, il y a une ride intéressante dans ce scénario spécifique. L'effet secondaire d'une exception levée est-il causé par une collection nulle ou un index hors plage considéré comme faisant partie du calcul du côté gauche de l'affectation, ou une partie du calcul de l'affectation elle-même? Java choisit ce dernier. (Bien sûr, c'est une distinction qui n'a d'importance que si le code est déjà erroné , car le code correct ne déréférencera pas null ou ne passera pas un mauvais index en premier lieu.)
Alors que se passe-t-il?
a[b]
est à gauche dub=0
, donc lesa[b]
exécutions en premier , résultant ena[1]
. Cependant, la vérification de la validité de cette opération d'indexation est retardée.b=0
se produit.a
est valide eta[1]
est à portée se produita[1]
produit en dernier.Donc, bien que dans ce cas spécifique il y ait quelques subtilités à considérer pour ces rares cas d'erreur qui ne devraient pas se produire dans un code correct en premier lieu, en général, vous pouvez raisonner: les choses à gauche se produisent avant les choses à droite . C'est la règle que vous recherchez. Parler de priorité et d'associativité est à la fois déroutant et hors de propos.
Les gens se trompent tout le temps , même ceux qui devraient être mieux informés. 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 beaucoup de gens aient des croyances complètement incorrectes sur la relation entre la préséance / associativité et l'ordre d'évaluation - à savoir qu'en réalité, il n'y a pas de telle relation. ; 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 éléments s'appliquent également à Java.
la source
La réponse magistrale d'Eric Lippert n'est cependant pas vraiment utile car elle parle d'une langue différente. Il s'agit de 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 savons tous qu'il est associatif à droite, oui?). Réduire un peu les choses qui nous intéressent dans cette question:[… Il décrit ensuite la signification réelle de la mission elle-même, que nous pouvons ignorer ici par souci 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 appel d'opérateur ou de méthode. Les affectations de tableaux sont l'un des cas les plus complexes, mais même là, il reste L2R. (Le JLS vous recommande ne pas écrire de code qui a besoin de ces sortes de contraintes sémantiques complexes , et moi aussi: vous pouvez avoir plus qu'assez de problèmes avec une seule affectation par instruction!)
C et C ++ sont définitivement 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 davantage d'optimisations. C # ressemble à Java apparemment, mais je ne connais pas assez bien sa littérature pour pouvoir pointer vers la définition formelle. (Cela varie vraiment selon la langue, Ruby est strictement L2R, tout comme Tcl - bien qu'il manque un opérateur d'affectation en soi pour des raisons non pertinentes ici - et Python est L2R mais R2L en ce qui concerne l'affectation , ce que je trouve étrange mais là vous allez .)
la source
a[-1]=c
,c
est évalué, avant-1
est reconnu comme invalide.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
(a[b]) = (b=0);
3) Selon 15.7. Ordre d'évaluation du JLS
et
Donc:
a)
(a[b])
évalué en premier àa[1]
b) puis
(b=0)
évalué à0
c)
(a[1] = 0)
évalué en dernierla source
Votre code équivaut à:
int[] a = {4,4}; int b = 1; c = b; b = 0; a[c] = b;
ce qui explique le résultat.
la source
Prenons un autre exemple plus détaillé ci-dessous.
En règle générale:
Il est préférable d'avoir un tableau des règles d'ordre de préséance et d'associativité disponible à lire 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é
Étape 1: Selon les règles de priorité: les opérateurs / et * ont priorité sur les opérateurs + -. Par conséquent, le point de départ pour exécuter cette équation sera réduit à:
100/10*2
Étape 2: Selon les règles et la priorité: / et * sont égaux en priorité.
Comme les opérateurs / et * sont égaux en priorité, nous devons examiner l'associativité entre ces opérateurs.
Selon les RÈGLES D'ASSOCIATIVITÉ 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 priorité: + et - sont égaux en priorité.
Nous devons maintenant regarder l'associativité entre les opérateurs + et - opérateurs. Selon l'associativité de ces deux opérateurs particuliers, nous commençons à exécuter l'équation de GAUCHE à DROITE, c'est-à-dire que 3 + 20 est exécuté en premier:
=3+20 =23 =23-13 =10
10 est la sortie correcte une fois compilée
Encore une fois, il est important d'avoir un tableau des règles d'ordre de préséance et d'associativité avec vous lorsque vous résolvez ces questions, par exemple http://introcs.cs.princeton.edu/java/11precedence/
la source
10 - 4 - 3
.+
est un opérateur unaire (qui a une associativité de droite à gauche), mais additif + et - ont juste comme multiplicatif * /% à gauche à la bonne associativité.