Каковы правила для порядка оценки в 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
бежит первым! Правила приоритета говорят, что индексирование имеет более высокий приоритет, чем присвоение, но это не означает, что индексатор работает до правого задание.правила приоритета и ассоциативности накладывают ограничения что:
- операция индексатора должна выполняться до операции, связанной с левой операцией назначения
- операция, связанная с правой операцией назначения, должна выполняться до операции, связанной с левой операцией назначения.
приоритет и ассоциативность только говорят нам, что присвоение ноль до
b
должно произойти до заданиеa[b]
. Старшинство и ассоциативность ничего не говорит о том,a[b]
оценивается до или после theb=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=С означает А=(B=С), которая присваивает значение C к B, а затем присваивает значение b, чтобы а.
(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 списки аргументов вычисляются слева-направодля Массивы: сначала вычисляются выражения измерений слева направо. Если какое-либо из вычислений выражения завершается внезапно, выражения справа от него не вычисляются.