Почему изменение приращения pre на post на итерационной части цикла for не имеет значения?
почему это
int x = 2;
for (int y =2; y>0;y--){
System.out.println(x + " "+ y + " ");
x++;
}
печатает то же самое, что и это?
int x = 2;
for (int y =2; y>0;--y){
System.out.println(x + " "+ y + " ");
x++;
}
насколько я понимаю, постинкремент сначала используется "как есть", а затем увеличивается. Предварительно инкремент сначала добавляется, а затем используется. Почему это не относится к телу цикла?
27 ответов
цикл эквивалентен:
int x = 2;
{
int y = 2;
while (y > 0)
{
System.out.println(x + " "+ y + " ");
x++;
y--; // or --y;
}
}
как вы можете видеть из чтения этого кода, не имеет значения, используете ли вы оператор post или pre decrement в третьем разделе цикла for.
В общем, любой для цикла формы:
for (ForInit ; Expression ; ForUpdate)
forLoopBody();
точно эквивалентно циклу while:
{
ForInit;
while (Expression) {
forLoopBody();
ForUpdate;
}
}
цикл for более компактен и, следовательно, легче анализировать для такой общей идиомы.
чтобы визуализировать эти вещи, разверните цикл for на цикл while:
for (int i = 0; i < 5; ++i) {
do_stuff(i);
}
расширяется:
int i = 0;
while (i < 5) {
do_stuff(i);
++i;
}
выполняете ли вы post-increment или pre-increment на счетчике цикла, не имеет значения, потому что результат выражения increment (либо значение до или после инкремента) не используется в том же операторе.
нет никакой разницы с точки зрения производительности, если это ваша забота. Он может использоваться только неправильно (и, следовательно, чувствителен к ошибкам), когда вы использовать это во время инкремент.
считаем:
for (int i = 0; i < 3;)
System.out.print(++i + ".."); //prints 1..2..3
for (int i = 0; i < 3;)
System.out.print(i++ + ".."); //prints 0..1..2
или
for (int i = 0; i++ < 3;)
System.out.print(i + ".."); //prints 1..2..3
for (int i = 0; ++i < 3;)
System.out.print(i + ".."); //prints 1..2
интересная деталь, однако, что нормальная идиома должна использовать i++
в выражении приращения for
оператор и что компилятор Java будет компилировать его, как если бы есть.
++i и I++ имеют значение при использовании в сочетании с оператором присваивания, таким как int num = I++ и int num = ++i или другими выражениями. В приведенном выше цикле для цикла существует только условие приращения, поскольку оно не используется в сочетании с любым другим выражением, это не имеет никакого значения. В этом случае это будет означать только i = i + 1.
этот цикл такой же, как этот while
петли:
int i = 0;
while(i < 5)
{
// LOOP
i++; // Or ++i
}
да, это должно быть то же самое.
эти два случая эквивалентны, потому что значение i сравнивается после выполнения инструкции increment. Однако, если вы сделали
if (i++ < 3)
и
if (++i < 3)
вам придется беспокоиться о порядке вещей.
и если вы сделали
i = ++i + i++;
тогда ты просто орехи.
потому что ничего в ваших примерах-это используя значение, возвращаемое из pre-или post-increments. Попробуйте обернуть System.out.println()
вокруг ++x
и x++
чтобы увидеть разницу.
С глава спецификации языка Java на for loops:
BasicForStatement:
for ( ForInit ; Expression ; ForUpdate ) Statement
... если присутствует часть ForUpdate, выражения вычисляются в последовательность слева направо; их ценности, если таковые имеются, отбрасываются. ... Если часть ForUpdate отсутствует, никаких действий не предпринимается.
(выделение мое).
выходные данные одинаковы, потому что элемент "increment" в "for (initial; comparison; increment)" не использует результат утверждения, он просто полагается на побочный эффект утверждения, которое в этом случае увеличивает "i", что одинаково в обоих случаях.
проверка выполняется до вычисления аргумента increment. Операция "инкремент"выполняется в конце цикла, даже если она объявлена в начале.
попробуйте этот пример:
int i = 6;
System.out.println(i++);
System.out.println(i);
i = 10;
System.out.println(++i);
System.out.println(i);
вы должны быть в состоянии выяснить, что он делает из этого.
потому что значение y
рассчитывается в for
оператор и значение x
рассчитывается в своей собственной строке, но в System.out.println
они только ссылаются.
если вы уменьшились внутри System.out.println
, вы получите другой результат.
System.out.println(y--);
System.out.println(--y);
здесь много хороших ответов, но в случае, если это поможет:
подумайте о y-- и --y как о выражениях с побочными эффектами или о заявлении, за которым следует выражение. y-- похоже на это (подумайте об этих примерах как о псевдо-сборке):
decrement y
return y
и --y делает это:
store y into t
decrement y
load t
return t
в вашем примере цикла вы отбрасываете возвращаемое значение в любом случае и полагаетесь только на побочный эффект (проверка цикла происходит после того, как оператор decrement выполняется; он не получает/не проверяет значение, возвращаемое декрементом).
если for
loop использовал результат выражения i++
или ++i
для чего-то, тогда это было бы правдой, но это не так, это просто потому, что его побочный эффект.
вот почему вы также можете поставить void
есть метод, а не просто числовое выражение.
инкремент выполняется как независимый оператор. Так что
y--;
и
-- y;
эквивалентны друг другу, и оба эквивалентно
y = y-1;
потому что это:
int x = 2;
for (int y =2; y>0; y--){
System.out.println(x + " "+ y + " ");
x++;
}
эффективно переводится компилятором на следующее:
int x = 2;
int y = 2
while (y > 0){
System.out.println(x + " "+ y + " ");
x++;
y--;
}
Как видите, используя y--
или --y
не приводит к какой-либо разницы. Было бы важно, если бы вы написали свой цикл так:
int x = 2;
for (int y = 3; --y > 0;){
System.out.println(x + " "+ y + " ");
x++;
}
это даст тот же результат, что и ваши два варианта цикла, но меняется с --y
до y--
здесь нарушит вашу программу.
Это дело вкуса. Они делают то же самое.
Если вы посмотрите на код классов java, вы увидите там for-loops с post-increment.
вы правы. Разницу можно увидеть в этом случае:
for(int i = 0; i < 5; )
{
System.out.println("i is : " + ++i);
}
Да, он делает это последовательно. Intialisation, то условие оценки, а если true, то выполняется тело и затем увеличивая.
префикс и постфикс разница будет заметна только при выполнении операции назначения с приращением / уменьшением.
различий нет, потому что каждая часть для "аргументов" - это отдельные операторы.
и что интересно, что компилятор может решить заменить простой пост-incrementations предварительно incrementations и это не будет ничего менять в коде.
Они ведут себя по-разному. Конструкция с i++ немного медленнее, чем с ++i потому что первое включает в себя возврат как старых, так и новых значений Я. С другой стороны, последний возвращает только старое значение Я.
тогда, вероятно, компилятор делает немного магии и изменяет любые изолированные i++ на ++i по соображениям производительности, но с точки зрения алгоритма raw они не совсем одинаковы.
в Stackoverflow есть много похожих сообщений:
- разница между i++ и ++i в цикле?
- есть ли разница в производительности между ++I и I++ в C#?
- есть ли разница в производительности между i++ и ++i в C?
однако, кажется, ваш вопрос более общий, потому что он не относится к какому-либо языку или компилятору. Большинство из вышеперечисленного вопросы касаются конкретного языка/компилятора.
вот краткое описание:
- если мы говорим о C / C++ / Java (возможно, C# тоже) и современном компиляторе:
- если
i
- целое число (const int
,int
, etc.):- тогда компилятор в основном заменит
i++
С++i
, потому что они семантически идентичны и поэтому не изменяют вывод. это можно проверить, проверив сгенерированный код / байт-код (для Java, я использую просмотр байт-кода jclasslib).
- тогда компилятор в основном заменит
- другое:
- если
- еще:
- все ставки отключены, потому что компилятор не может гарантировать, что они семантически идентичны, поэтому он не пытается оптимизировать.
поэтому, если у вас есть класс В C++, который переопределяет операторы postfix и prefix (например,std::iterator
), такая оптимизация крайне редко, сделанный.
в итоге:
- думай, что говоришь, и говори, что имеешь в виду. Для части приращения
for
петли, вы почти всегда хотите версию префикса (т. е.++i
). - компилятор switcheroo между
++i
иi++
не всегда можно сделать, но он попытается сделать это за вас, если сможет.
в цикле сначала инициализация, затем Проверка условий, затем выполнение, после этого приращения/уменьшения. таким образом, pre/post increment / decrement не влияет на код программы.
о i++ (пост-приращение) и ++я (предварительно приращения) @мне: "в обоих случаях выражение оценивается, и результат используется для проверки состояния. В случае pre-increment выражение increment увеличивает переменную и возвращает результирующее значение. Для post-increment выражение increment также увеличивает переменную, но возвращает Предыдущее значение. В результате pre-increment сравнивается с инкрементным значением, тогда как post-increment сравнивает против исходного значения; в обоих случаях переменная была увеличена при проверке условия."- tdammers
существует довольно путаница между оператором post и pre increment, это можно легко понять из этого отрывка "алгоритмы, 4-е издание Роберта Седжвика и Кевина Уэйна"
операторы инкремента/декремента: i++ совпадает с i = i + 1 и имеет значение я в выражении. Аналогично, я -- это то же самое, что i = i - 1. Код ++i и -- i одинаковы, за исключением того, что значение выражения берется после приращения/ декремент, не до.
x = 0;
post increment:
x++;
step 1:
assign the old value (0) value of the x back to x.So, here is x = 0.
step 2:
after assigning the old value of the x, increase the value of x by 1. So,
x = 1 now;
when try to print somthing like:
System.out.print(x++);
the result is x : 0. Because only step one is executed which is assigning
old value of the x back and then print it.
But when, we do operation like this:
i++;
System.out.print(i);
the result is x: 1. which is because of executing Step one at first
statement and then step two at the second statement before printing the
value.
pre increment:
++x;
step 1:
increase the value of x by 1. So, x = 1 now;
step 2:
assign the increased value back to x.
when try to print something like:
System.out.print(++1)
the result is x : 1. Because the value of the x is raised by 1 and then
printed. So, both steps are performed before print x value. Similarly,
executing
++i;
system.out.print(i);
Both steps are executed at statement one. At second statement, just the
value of "i" is printed.