WEB-школа Java

Эта тема как пример урока не совсем хороша, поскольку за ней скрывается непростая для понимания идиома Java. Тем не менее, максимально прояснив вопрос, я сохранил эту страницу как метку уровня: на нескольких квадратных сантиметрах даются фундаментальнейшие понятия.

Главная Обзор систем Java Incognita Cтудент Пробные уроки Коммуникатор Вниз

Пробный урок: Оператор инкремента

raquette
Это фрагмент одного из уроков, который должен помочь Вам составить свое мнение о стиле и ка­чест­ве предлагаемых учебных материалов. На данном примере Вы можете заме­тить, что рассмотрение вопросов более углубленное, чем это обычно бывает в учебниках, поэтому, выполнив программу обучения, Вы станете «продвинутым» специалистом по Java. Если Вы никогда не програм­мировали и ничего здесь не поймете (cкорее всего, Вам непонятна будет программа, написаннная на Java), это еще не значит, что не стоит браться за дело. Здесь показан лишь фрагмент одного, причем не первого урока.

Операторы – это самый древний раздел программирования, своего рода классика, которая была, есть и будет в любом языке. За кажущейся простотой операторов скрывается фундаментальная глубина языка и Вы поймете это рассмотрев пример. Неслучайно, что даже парни, считающие себя крутыми и программи­рующие последние лет 10 -15, подчас совершают грубые ошибки при работе с оператора­ми.

Оператор - это знак или слово, в котором зашифровано некое действие, которое будет совершено с операндом - выражением, которое помещено рядом с оператором. Это действие может оказаться весьма загадочным, а его результаты неочевидными, поэтому с операторами надо не просто ознакомиться теоретически, но и всесторонне исследовать их. Для простоты я здесь ограничусь рассмотрением инкремента,- декремент абсолютно симметричен инкременту и его можно изучить самостоятельно в качестве упражнения. Выполняя операцию инкремента, программа вычисляет числовой операнд только один раз, а затем дает приращение на единицу полученному значению. Таким образом операция позволяет избежать возможных неоднократных побочных эффектов при вычислении выражения операнда. Ирония в том, что это делается с помощью умышленного применения другого побочного эффекта (что называется "...упал вдругорядь - уж нарочно!").

В уроке даются понятия выражения, значения выражения, побочного эффекта

Операторы инкремента и декремента

Обозначаются ++ (инкремент) и - - (декремент). Итак, пусть мы имеем:

                int х = 1;
++х;
Любой, взглянув на это, решит, что цель выражения с оператором инкремента очевидна - дать приращение x на единицу. Теперь можно сказать, что значением выражения ++х является 1+1=2. Все вроде бы просто, однако не совсем: оператор инкремента имеет на входе одно значение: х, а на выходе - два. Первое значение на выходе - это наращенный х, которого в явном виде здесь нет, но программисту о нем хорошо известно (более того, зачастую программист полагает что ++х - это и есть изображение наращенного х). Второе значение на выходе - это значение выражения ++х. Парадокс состоит в том, что оно совершенно явно присутствует (имеет выражение!), но программист о нем не подозревает. Этот парадокс "очевидности" создает проблему, поскольку оператор инкремента (декремента) может стоять не только перед операндом в виде префикса: ++x, как в приведенном примере, но и с противоположной стороны – в виде постфикса: x++.
                int х = 1;
                ++х;
                х++;
Обе эти формы наращивают х ровно на единицу, то есть это значение не имеет вариантов - оно будет равно 2. Однако, второе, генерируемое инкрементом, - прямое значение выражения, будет разным для постфикса и префикса. Префиксная формула принимает значение наращенного х, а постфиксная принимает значение первоначального х. То есть ++x примет значение 2, а x++ примет значение 1. В этих нескольких предложениях суть операторов декремента и инкремента изложена достаточно ясно, к сожалению, этого недостаточно и останавливать изложение вопроса на этом пункте нельзя. Это - теория, но усваивать ее лучше на практике - во время написания тестовых программ. 

Упражнение 1.

Напишите программу, которая печатает:
  1.      х до инкремента

  2.       ++x

  3.       х после инкремента

Упражнение 2.

Напишите программу которая печатает:
  1.      x до инкремента
  2.      х++
  3.       х после инкремента

/* Такие упражнения Вы должны выполнять самостоятельно. Если в течение 3 дней напряженных усилий это Вам не удастся, пишите и я пришлю вам инструкцию. А на этот раз я покажу, как это делается. Вы должны написать нечто подобное:

*//* Alim Azkan’s distinction 
    between an increment expression value and an operational side effect result 
    */
   class Inc2 {
        public static void main ( String arg [] ) {
                int num; //мы будем хранить здесь инкрементируемое
                        // число введенное в ком.строке 
                num = Integer.parseInt( arg [0] ) ;//превращение аргумента ком.строки в число
                System.out.println (" num = "+ num );
                System.out.println (" num = "+ (num++) );
                System.out.println (" num = "+ num );
        }

   }
После компиляции (javac Inc2.java) и запуска (java Inc2 2, где 2- это аргумент командной строки, то есть число, которое мы собираемся инкрементировать) вывод будет такой:

        num++ = 2
        num = 3
Первая строка показывает входное значение операнда – переменной х. В резуль­тате дей­ствия оператора ++ мы наблюдаем два разных выходных значения. Удивительно, не так ли!? Во второй строке вывода напечатано значение выражения num++, а в третьей – новое значение переменной х.

Изменение значения операнда (здесь это переменная x) в результате операции называется побочным эффектом. С точки зрения Java, целью применения оператора было получение значения выражения x++. Но внешне запись выражения x++ выглядит как воздействие на операнд с целью его изменения и кажется, что именно этот побочный эффект и является целью операции. Java-программист должен научиться видеть любую запись как цельное выражение, эквивалентом которого служит его значение. Таковы правила игры Java, вводимые через реализацию (воплощение) компилятора.

Если вы не зафиксируете значение выражения x++, оно пропадёт. В примере мы за­фиксировали его, передав как аргумент в функцию println. Это значение можно также зафиксировать, сохранив его в другой переменной, например, y.

Побочный эффект действия инкремента префиксной и постфиксной форм одинаков: как ++x, так и x++ дают одно и тоже значение, сохраняемое дефолтом (по умолчанию) в х. Однако значение выражений ++x и x++ - это совсем другое. Чтобы увидеть разницу, надо зафиксировать указанные значения. Обычно это делается так: если y = x++, то результатом выражения будет пер­воначальное значение x, которое и будет зафиксировано в переменной у. В этом случае значение выражения (переменная у) и значение переменной x после операции будут различаться. Если же y = ++x, то результатом выражения будет инкремент значения x, то есть значения выражения (переменная у) и результат побочного эффекта будут совпадать.

Из дальнейшего рассмотрения станет ясно, что повсеместно применяемые иллю­страции инкремента в форме 

        y = ++x;
        y = x++; 
не дают о нем точного и ясного представ­ления, ввиду указанного выше парадокса очевидности. В каждом случае: и в префиксе, и в постфиксе - оба значения, генерируемые каждым оператором, фиксируются: в y сохраняется результат операции инкремента, а в х, по умолчанию, сохраняется результат побочного эффекта. Это скрывает природу инкремента. Чтобы избежать этого, надо фиксировать только одно из значений, причем заставить систему сделать выбор - какое именно.

Для качественного усвоения темы предлагается решить задачу 1. Не читайте дальше, пока не решите ее!

_________________________________________________________________________

Задача 1. Определить значение переменной x в результате вычисления каждого из выражений:

        x = ++x;
        x = x++;
  

Уверен, что ответ будет для Вас неожиданным. Если читать очень внимательно и приме­нять «логику программиста» (или «логику прохвоста»: просто инвертируя ожидаемый ответ), можно ответить правильно. Не читая дальше, выполните упражнение 3.

Упражнение 3.

Проверьте свои ответы, изменив программы упражнений 1 и 2 так, чтобы они выводили значения выражений и переменных задачи 1.

Ответ к задаче 3:

Выражение x = ++x  дает инкрементированное значение x, что вполне предсказуемо, а вот в случае x = x++, значение переменной x, после всего, останется таким, каким оно было до операции! То есть, результатом операции инкремента будет просто старое значение x, оно и будет присво­ено переменной x, инкрементированное же значение x не фиксируется (эффект-то – побочный!) и пропадает. 

Рассмотрим вычисление x = x++ по шагам. Вначале вычисляется значение выражения x++: оно равно первоначальному значению x. Затем берется значение переменной x и ему дается приращение на единицу. Из двух имеющихся значений в переменную x устанавливается значение выражения x++, то есть старое значение x, а не наращенный x. Второе значение – инкремент x – ничему не присваивается – место уже занято и пропадает! x не получает приращения - это сенсация для подавляющего большинства программистов - ведь всем очевидно, что х должен был получить приращение, а этого не произошло! Однако, все логично: в коде имеется явное указание программиста и оно имеет приоритет над дефолтом (действием по умолчанию).

Примечания. Рационалист, чтобы не путаться, может

                y = x++; 
применить пару выражений:
                y = x;
                x = x +1; 
(форма x += 1; имеет нюанс, но об этом в основном курсе) В данном случае, эта пара будет эквивалентна операции постфиксного инкремента. Но, что если x – какое-то выражение, при вычис­лении которого возникает побочный эффект? Тогда последовательные вычисления этого выражения будут давать разные значения. Операторы инкремента и декремента как раз и были придуманы для того, чтобы выражение x гаран­тированно вычисля­лось лишь однажды. Один побочный эффект уничтожается другим. В ориентации на выражения Java более последователен, чем С; в нем существует четкий порядок работы с выражениями, поэтому там где С демонстрирует неопределенное поведение (допускаемое его стандартом?!), - Java вполне логичен. Страуструп – креатор «С++», писал, что его язык любят и ненавидят за ориентацию на выражения. В этом отношении Java будет «покруче», чем «С++», поэтому его должны любить еще больше.

Задача 4. Установить к каким типам Java можно применять оператор инкремента Написать для этого соответствующие программы. Инкременту должен подвергаться аргумент командной строки.

Задача 5. Придумайте пример неэквивалентности двух способов расчета:

x = x +1 и x = x++

составьте иллюстрирующую его программу.

Задача 5. Вычислить значения, которые примут переменные после выполнения кода:

                int a, b=5, c=7, d=9;
                a = b++ +c++ + ++d;

Проверить в программе. Какие пробелы среди плюсов во втором выражении можно убрать, а какие нет.

                int a, b=5, c=7, d=9;
                a = b++ + ++c+ ++d;

Задание аналогичное

Главная
Обзор систем
Java Incognita
Cтудент
Пробные уроки
Коммуникатор
Вверх

Хостинг от uCoz