Главная | Обзор систем | Java Incognita | Cтудент | Пробные уроки | Коммуникатор | Вниз |
Операторы – это самый древний раздел программирования, своего рода классика, которая была, есть и будет в любом языке. За кажущейся простотой операторов скрывается фундаментальная глубина языка и Вы поймете это рассмотрев пример. Неслучайно, что даже парни, считающие себя крутыми и программирующие последние лет 10 -15, подчас совершают грубые ошибки при работе с операторами.
Оператор - это знак или слово, в котором зашифровано некое действие, которое будет совершено с операндом - выражением, которое помещено рядом с оператором. Это действие может оказаться весьма загадочным, а его результаты неочевидными, поэтому с операторами надо не просто ознакомиться теоретически, но и всесторонне исследовать их. Для простоты я здесь ограничусь рассмотрением инкремента,- декремент абсолютно симметричен инкременту и его можно изучить самостоятельно в качестве упражнения. Выполняя операцию инкремента, программа вычисляет числовой операнд только один раз, а затем дает приращение на единицу полученному значению. Таким образом операция позволяет избежать возможных неоднократных побочных эффектов при вычислении выражения операнда. Ирония в том, что это делается с помощью умышленного применения другого побочного эффекта (что называется "...упал вдругорядь - уж нарочно!").
В уроке даются понятия выражения, значения выражения, побочного эффекта
Обозначаются ++ (инкремент) и - - (декремент). Итак, пусть мы имеем:
int х = 1;Любой, взглянув на это, решит, что цель выражения с оператором инкремента очевидна - дать приращение x на единицу. Теперь можно сказать, что значением выражения ++х является 1+1=2. Все вроде бы просто, однако не совсем: оператор инкремента имеет на входе одно значение: х, а на выходе - два. Первое значение на выходе - это наращенный х, которого в явном виде здесь нет, но программисту о нем хорошо известно (более того, зачастую программист полагает что ++х - это и есть изображение наращенного х). Второе значение на выходе - это значение выражения ++х. Парадокс состоит в том, что оно совершенно явно присутствует (имеет выражение!), но программист о нем не подозревает. Этот парадокс "очевидности" создает проблему, поскольку оператор инкремента (декремента) может стоять не только перед операндом в виде префикса: ++x, как в приведенном примере, но и с противоположной стороны – в виде постфикса: x++.
++х;
int х = 1; ++х; х++;Обе эти формы наращивают х ровно на единицу, то есть это значение не имеет вариантов - оно будет равно 2. Однако, второе, генерируемое инкрементом, - прямое значение выражения, будет разным для постфикса и префикса. Префиксная формула принимает значение наращенного х, а постфиксная принимает значение первоначального х. То есть ++x примет значение 2, а x++ примет значение 1. В этих нескольких предложениях суть операторов декремента и инкремента изложена достаточно ясно, к сожалению, этого недостаточно и останавливать изложение вопроса на этом пункте нельзя. Это - теория, но усваивать ее лучше на практике - во время написания тестовых программ.
/* Такие упражнения Вы должны выполнять самостоятельно. Если в течение 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:
Выражение 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тудент | Пробные уроки |
Коммуникатор | Вверх |