Каковы правила для порядка оценки в Java?

Я читаю некоторый текст Java и получил следующий код:

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

в тексте автор не дал четкого объяснения и эффект последней строки таков:a[1] = 0;

Я не уверен, что понимаю: как произошла оценка?

6 ответов


позвольте мне сказать это очень ясно, потому что люди все время неправильно понимают это:

порядок оценки подвыражений независимая ассоциативности и приоритета. Ассоциативность и приоритет определяют, в каком порядке операторы выполнены, но не определите, в каком порядке подвыражения оцениваются. Ваш вопрос о порядке, в котором подвыражения оцениваются.

считают A() + B() + C() * D(). Умножение имеет более высокий приоритет, чем сложение, и добавление левую ассоциативность, так что это эквивалентно (A() + B()) + (C() * D()) но знание этого только говорит вам, что первое сложение произойдет до второго сложения, и что умножение произойдет до второго сложения. он не говорит вам, в каком порядке будут называться A(), B(), C() и D ()! (он также не говорит вам, есть ли умножение происходит до или после первого сложения.) Было бы вполне возможно подчиняться правилам приоритет и ассоциативность при компиляции это:

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

там соблюдаются все правила приоритета и ассоциативности - первое сложение происходит до второго сложения, а умножение происходит до второго сложения. Ясно, что мы можем делать вызовы A(), B (), C() и D () в любой приказывайте и все еще повинуйтесь правила приоритета и ассоциативности!

нам нужно правило связаны к правилам приоритета и ассоциативности, чтобы объяснить порядок, в котором оцениваются подвыражения. соответствующим правилом в Java (и C#) является "подвыражения оцениваются слева направо". поскольку A () появляется слева от C (), сначала вычисляется A (),независимо от того, что C () участвует в умножении, а A () участвует только в дополнение.

Итак, теперь у вас достаточно информации, чтобы ответить на ваш вопрос. В a[b] = b = 0 правила ассоциативности говорят, что это a[b] = (b = 0); но это не значит, что b=0 бежит первым! Правила приоритета говорят, что индексирование имеет более высокий приоритет, чем присвоение, но это не означает, что индексатор запускается перед самым правым назначением.

правила приоритета и ассоциативности накладывают ограничения что:

  • операция индексатора должна выполняться перед операцией, связанной с операцией назначения слева
  • операция, связанная с операцией назначения правой руки, должна выполняться до операции назначения левой руки.

приоритет и ассоциативность только говорят нам, что назначение нуля to b должно произойти до задание a[b]. Старшинство и ассоциативность ничего не говорит о том,a[b] оценивается до или после the b=0.

опять же, это то же самое, что:A()[B()] = C() -- все, что мы знаем, это то, что индексация должна произойти до назначения. Мы не знаем, запускается ли A (), B () или C() первым на основе приоритета и ассоциативности. Нам нужно другое правило, чтобы понять это.

правило, опять же, "когда у вас есть выбор о том, что делать во-первых, всегда идите слева направо": the a[b] налево на b=0, так что a[b] работает первый, в результате a[1]. Тогда b=0 происходит, а затем присвоение значения!--14--> происходит в прошлом.

вещи слева происходят до того, как вещи справа. Это правило, которое вы ищете. Разговоры о приоритете и ассоциативности одновременно сбивают с толку и неуместны.

люди понимают это неправильно все время, даже люди, которые должны знать лучше. Я отредактировал слишком много книги по программированию, которые неправильно сформулировали правила, поэтому неудивительно, что многие люди имеют совершенно неправильные представления о взаимосвязи между приоритетом/ассоциативностью и порядком оценки, а именно, что на самом деле такой связи нет; они независимы.

если эта тема вас интересует, см. мои статьи по этой теме для дальнейшего чтение:

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

они о C#, но большая часть этого материала одинаково хорошо относится к Java.


мастерский ответ Эрика Липперта, тем не менее, не очень полезен, потому что он говорит о другом языке. Это Java, где спецификация языка Java является окончательным описанием семантики. В частности, §15.26.1 имеет значение, потому что это описывает порядок оценки для = оператор (мы все знаем, что это право-ассоциативной, да?). Сократив его немного до бит, которые мы заботимся в этом вопросе:

если выражение левого операнда является выражением доступа к массиву (§15.13), то требуется много шагов:

  • сначала вычисляется подвыражение ссылки на массив выражения доступа к массиву левого операнда. Если эта оценка завершается резко, то выражение присвоения завершается резко по той же причине; индексного подвыражения (левого операнда выражения доступа массива) и правый операнд не вычисляется и никакого назначения не происходит.
  • в противном случае вычисляется подвыражение индекса выражения доступа к массиву левого операнда. Если эта оценка завершается резко, то выражение присвоения завершается резко по той же причине, что и правый операнд не вычисляется, и никакое присвоение не происходит.
  • в противном случае вычисляется правый операнд. Если эта оценка завершается резко, то выражение присвоения завершается резко по той же причина и назначение не происходит.

[...затем он переходит к описанию фактического значения самого задания, которое мы можем игнорировать здесь для краткости...]

короче говоря, Java имеет очень четко определенный порядок оценки это в значительной степени точно слева направо в аргументах для любого оператора или вызова метода. Назначения массивов являются одним из более сложных случаев, но даже там это все еще L2R. (JLS делает рекомендую вам не пишите код, который нуждается в таких сложных семантических ограничениях, и я тоже: вы можете попасть в более чем достаточно неприятностей только с одним заданием на заявление!)

C и c++ определенно отличаются от Java в этой области: их определения языка оставляют порядок оценки неопределенным намеренно, чтобы включить больше оптимизаций. C#, по-видимому, похож на Java, но я не знаю его литературу достаточно хорошо, чтобы иметь возможность указать на формальный определение. (Это действительно зависит от языка, хотя Ruby строго L2R, как и Tcl, хотя в нем отсутствует оператор присваивания per se по причинам, не относящимся здесь - и Python является L2R, но R2L в отношении назначения Это странно, но там вы идете.)


a[b] = b = 0;

1) оператор индексирования массива имеет более высокий приоритет, чем оператор присваивания (см. ответ):

(a[b]) = b = 0;

2) Согласно 15.26. Операторы присваивания JLS

существует 12 операторов присваивания; все они синтаксически ассоциативны справа налево (они группируются справа налево). Таким образом, a=b=c означает a=(b=c), который присваивает значение c b, а затем присваивает значение b a.

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

3) Согласно 15.7. Порядок оценки JLS

язык программирования Java гарантирует, что операнды операторов, кажется, оцениваются в определенном порядке оценки, а именно, слева направо.

и

левый операнд двоичного оператора, по-видимому, полностью вычисляется перед вычислением любой части правого операнда.

так:

a)(a[b]) оценивали сначала a[1]

B), то (b=0) оценка для 0

c)(a[1] = 0) оценка последнего


ваш код эквивалентен:

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

что и объясняет результат.


рассмотрим другой более глубокий пример ниже.

как правило:

лучше всего иметь таблицу правил приоритета и ассоциативности, доступную для чтения при решении этих вопросов, например http://introcs.cs.princeton.edu/java/11precedence/

вот хороший пример:

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

вопрос: что выход вышеуказанной строки?

ответ: применить правила приоритета и Ассоциативность

Шаг 1: в соответствии с правилами приоритета: / и * операторы имеют приоритет над + - операторами. Поэтому начальная точка для выполнения этого уравнения будет сужена до:

100/10*2

Шаг 2: в соответствии с правилами и приоритетом: / и * равны по приоритету.

как / и * операторы равны по приоритету, нам нужно посмотреть на ассоциативность между этими операторами.

согласно правилам ассоциативности этих два конкретных операторов, мы начинаем выполнять уравнение слева направо, т. е. сначала выполняется 100/10:

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

Шаг 3: уравнение теперь находится в следующем состоянии выполнения:

=3+20-13

в соответствии с правилами и приоритетом: + и - равны по приоритету.

теперь нам нужно посмотреть на ассоциативность между операторами + и - операторов. В соответствии с ассоциативностью этих двух частных операторов, мы начинаем выполнение сначала выполняется уравнение слева направо, т. е. 3+20:

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

10-правильный вывод при компиляции

опять же, важно иметь таблицу правил очередности и ассоциативности с вами при решении этих вопросов, например http://introcs.cs.princeton.edu/java/11precedence/


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

Он будет печатать 30 20 30

оператор iA[i] = i = 30; будет обработан следующим образом:

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

вот что говорит JLS об этом:

1 Сначала Оцените Левый Операнд
2 вычислит перед операцией
3 Оценка уважает скобки и приоритет
4 списка аргументов оцениваются слева направо

для Массивы: во-первых, выражения измерений вычисляются слева-направо. Если какая-либо из оценок выражения завершается внезапно, выражения справа от него не оцениваются.