Шрифт:
int main (int argc, char argv[]) { NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; int year, rem_4, remJOO, remJOO; NSLog (@"Enter the year to be tested:"); scant ("%i", &year); rem J = year % 4; remJOO = year % 100; remJOO = year % 400; if ( (rem 4 == 0 && remJOO != 0) || remJOO == 0 ) NSLog ((ant’s a leap year."); else NSLog (@"Nope, it’s not a leap year."); [pool drain]; return 0; } Вывод программы 6.5 Enter the year to be tested: (Введите год для проверки) 1955 Nope, it’s not a leap year. (Нет, это не високосный год) Вывод программы 6.5 (повторный запуск) Enter the year to be tested: 2000 It’s a leap year. (Это високосный год) Вывод программы 6.5 (повторный запуск) Enter the year to be tested: 1800 Nope, it’s not a leap year. (Нет, это не високосный год) В приведенных примерах вводится 1955-й год, который не является високосным, поскольку он не делится нацело на 4, 2000-й год, который является високосным, поскольку он делится нацело на 400, и 1800-й год, который не является високосным, поскольку он делится нацело на 100, но не на 400. В качестве последнего варианта следует проверить год, который делится нацело на 4, но не на 100. Выполните эту проверку самостоятельно. Objective-C дает программисту очень высокий уровень гибкости при формировании выражений. Например, в приведенной выше программе вместо вычисления промежуточных результатов rem_4, remJOO и rem_400 можно было бы сделать все непосредственно в операторе if: if (( year % 4 == 0 && year % 100 != О ) || year % 400 == 0 ) Использование пробелов для разделения отдельных операторов делает это выражение более удобным для чтения. Если игнорировать эти возможности и удалить необязательный набор круглых скобок, то получится следующее выражение: if(year%4==0&&year%100!=0)||year%400==0) Это выражение действует точно так же, как предыдущее выражение, однако дополнительные пробелы очень упрощают восприятие составных выражений. Вложенные операторы if
При рассмотрении общего формата оператора if мы указывали, что если результатом оценки выражения в круглых скобках является значение TRUE, то выполняется следующий оператор. Этим программным оператором может быть еще один оператор if, как в следующих строках. if ( [chessGame isOver] == NO ) ([игра в шахматы окончена] == НЕТ) if ( [chessGame whoseTurn] == YOU )([... чей ход] == ВАШ) [chessGame yourMove]; ([... ваш ход]) Если значение, возвращаемое после отправки сообщения isOver объекту chessGame, равно N0, то выполняется следующий оператор; в свою очередь, этот оператор является еще одним оператором if. Этот оператор if сравнивает значение, возвращаемое методом whoseTurn, со значением YOU. Если эти два значения равны, то объекту chessGame отправляется сообщение yourMove. Таким образом, сообщение yourMove отправляется только в том случае, если игра не окончена, и это ваш ход. Эти операторы можно было бы записать с помощью составной операции сравнения: if ( [chessGame isOver] == N0 && [chessGame whoseTurn] == YOU ) [chessGame yourMove]; Обычно с вложенными операторами if используют предложение else, как показано ниже. if ( [chessGame isOver] == N0 ) ([игра в шахматы окончена] == НЕТ) if ( [chessGame whoseTurn] == YOU )([... чей ход] == ВАШ) [chessGame yourMove]; ([... ваш ход]) else [chessGame my Move]; ([... мой ход]) Сначала все выполняется, как и раньше, но если игра не окончена и ход не ваш, то выполняется предложение else. В результате сообщение myMove отправляется объекту chessGame. Если игра окончена, то пропускается весь следующий оператор if вместе с присоединенным к нему предложением else. Предложение else связано с оператором if, который проверяет значение, возвращаемое методом whoseTurn, а не с оператором if, который проверяет, окончена ли игра (chessGame isOver). Общее правило состоит в том, что предложение else всегда связывается с последним оператором if, не содержащим else. Можно продвинуться еще на один шаг и добавить предложение else к внешнему оператору if. Это предложение else выполняется, если игра окончена. if ( [chessGame isOver] == NO ) ([игра в шахматы окончена] == НЕТ) if ( [chessGame whoseTurn] == YOU )([... чей ход] == ВАШ) [chessGame yourMove]; ([... ваш ход]) else [chessGame myMove]; ([...мой ход]) else [chessGame finish]; (... конец) Конечно, система может интерпретировать этот оператор и по-другому. Например, если в предыдущем примере удалить первое предложение else, то оператор не будет интерпретироваться в соответствии с показанными отступами. if ( [chessGame isOver] == NO ) if ( [chessGame whoseTurn] == YOU ) [chessGame yourMove]; else [chessGame finish]; Вместо этого оператор будет интерпретироваться следующим образом: if ( [chessGame isOver] == NO ) if ( [chessGame whoseTurn] == YOU ) [chessGame yourMove]; else [chessGame finish]; Предложение else связывается с последним оператором if, не включающим else. Можно использовать фигурные скобки, чтобы связать else с внешним if, а не с внутренним. Фигурные скобки замыкают оператор if, который находится внутри них. Нужная последовательность задается с помощью следующего оператора. if ( [chessGame isOver] == NO ) { if ( [chessGame whoseTurn] == YOU ) [chessGame yourMove]; } else [chessGame finish]; Конструкция else if
Вы уже видели, как действует оператор else при проверке двух возможных условий. Однако решения, которые приходится реализовать в программировании, не всегда ограничиваются выбором между двумя вариантами. Рассмотрим программу, которая выводит -1, если пользователь вводит отрицательное число; О, если число равно нулю; и 1, если число больше нуля. (Это реализация функции sign.) Очевидно, что в этом случае нужны три проверки: для отрицательных чисел, чисел, равных нулю, и положительных чисел. Простая конструкция if-else в данном случае не подходит. Конечно, мы могли бы прибегнуть здесь к трем отдельным операторам if, но это решение не всегда можно реализовать, особенно если проверки не являются взаимоисключающими. Чтобы справиться с этой ситуацией, можно добавить оператор if к предложению else. Мы уже говорили, что после предложения else может следовать любой допустимый программный оператор Objective-C, так почему не использовать еще один if? if (выражение 1 ) программный оператор 1 else if (выражение 2) программный оператор 2 else программный оператор 3 В результате оператор if расширяется — вместо логического решения с двумя значениями мы получаем логическое решение с тремя значениями. Вы можете продолжить добавление операторов if к предложениям else в том же стиле, чтобы получить логическое решение с п значениями. Эта конструкция так часто используется, что обычно ее называют конструкцией else if и форматируют так. if (выражение 1 ) программный оператор 1 else if (выражение 2 ) программный оператор 2 else программный оператор 3 Это способ делает чтение оператора более удобным. В следующей программе показано использование конструкции else if с реализацией описанной выше функции sign. Программа 6.6 // Реализация функции sign import
int main (int argc, char argv[]) { NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; int number, sign; NSLog (@"Please type in a number:"); scant ("%i", &number); if ( number < 0 ) sign = -1; else if ( number == 0 ) sign = 0; else // Положительное число sign = 1; NSLog (@"Sign = %i", sign); [pool drain]; return 0; } Вывод программы 6.6 Please type in a number: (Введите число) 1121 Sign = 1 Вывод программы 6.6 (повторный запуск) Please type in a number: -158 Sign = -1 Вывод программы 6.6 (повторный запуск) Please type in a number: О Sign = О Если введенное число меньше нуля, переменной sign присваивается значение -1; если число равно нулю, sign присваивается значение 0; в противном случае число больше нуля, поэтому sign присваивается значение 1. В следующей программе символ, введенный с терминала, анализируется и классифицируется как алфавитный символ (a-z или A-Z), как цифровой символ (0-9) или как специальный символ (все остальные символы). Для чтения одного символа с терминала при вызове scant используются символы форматирования %с. Программа 6.7 // Эта программа классифицирует символ, // введенный с клавиатуры import
int main (int argc, char argv[]) { NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; char c; NSLog (@"Enter a single character:"); scant ("%c", &c); it ( (c >= ’a' && c <= 'z') || (c >= ’A' && c <= 7')) NSLog (@"lt’s an alphabetic character."); else it ( c >= ’O’ && c <= ’9’ ) NSLog ((gilt's a digit."); else NSLog (@"lt’s a special character."); [pool drain]; return 0; } Вывод программы 6.7 Enter a single character: (Введите один символ) & It’s a special character. (Это специальный символ) Вывод программы 6.7 (повторный запуск) Enter a single character: 8 It’s a digit. (Это цифра) Вывод программы 6.7 (повторный запуск) Enter a single character: В It’s an alphabetic character. (Это алфавитный символ) В первой проверке после чтения символа мы определяем, является ли этот символ алфавитным (в переменной с типа char). Сначала проверяется, попадает ли этот символ в диапазон строчных букв: ( с >= 'а' && с <= 'z ') Это выражение имеет значение TRUE, если с находится в диапазоне символов от ’а’ до У (с — строчная латинская буква). Вторая проверка выполняется с помощью следующего выражения: ( с >= ’А’ && с <= “Z" ) Это выражение имеет значение TRUE, если с находится в диапазоне символов от 'А' до 'Т (с — прописная латинская буква). Эти проверки подходят для символов в кодировке ASCII. Если переменная с содержит алфавитный символ, проверка проходит успешно и выводится сообщение «It’s an alphabetic character» (Это алфавитный символ). В противном случае выполняется предложение else if. Оно определяет, является ли данный символ цифрой. Отметим, что выполняется сравнение с символами от '0' до ’9’, а нес цифрами от 0 до 9. Дело в том, что с терминала читается символ, а запись символов от '0' до '9' в компьютере отличается от чисел 0-9. В кодировке ASCII символ ’0' представляется во внутренней записи числом 48, символ Т — числом 49, и т.д. Если с — символ цифры, то выводится фраза «It’s a digit» (Это цифра). В противном случае (если с не является алфавитным символом или символом цифры) выполняется последнее предложение else, и на терминале выводится фраза «It’s a special character» (Это специальный символ). Затем выполнение программы завершается. Хотя в данном случае scant используется для чтения только одного символа, вы все равно должны нажать клавишу Enter после ввода символа, чтобы результат ввода был передан в программу. Обычно при чтении данных с терминала программа «не видит» введенных данных, пока не нажата клавиша Enter. В следующем примере напишем программу, которая позволяет ввести простое выражение в следующей форме: число оператор число Программа выполнит оценку выражения и выведет результаты на терминал. Операторы, которые нужно распознавать, — это обычные оператор сложения, вычитания, умножения и деления. Мы будем использовать класс Calculator из программы 4.6 главы 4. Каждое выражение будет передаваться этому калькулятору для вычислений. В следующей программе используется довольно большой оператор if с несколькими предложениями else if, определяющими, какая операция должна быть выполнена. Примечание. Чтобы избежать проблем с внутреннм представлением, лучше использовать процедуры из стандартной библиотеки islower и isupper. Для этого включите в программу строку #import . Программа 6.8 // Программа вычисления простых выражений в форме // число оператор число // Реализация класса Calculator import
@interface Calculator: NSObject { double accumulator; } // методы для сумматора (accumulator) -(void) setAccumulator: (double) value; -(void) clear; -(double) accumulator; // арифметические методы -(void) add: (double) value; -(void) subtract: (double) value; -(void) multiply: (double) value; -(void) divide: (double) value; @end @implementation Calculator -(void) setAccumulator: (double) value { accumulator = value; } -(void) clear { accumulator = 0; } -(double) accumulator { return accumulator; } -(void) add: (double) value { accumulator += value; } -(void) subtract: (double) value { accumulator -= value; } -(void) multiply: (double) value { accumulator = value; } -(void) divide: (double) value { accumulator /= value; } @end int main (int argc, char argv[]) { NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; double value 1, value2; char operator; Calculator deskCalc = [[Calculator alloc] init]; NSLog (@"Type in your expression."); scant ("%lf %c %lf", &value1, &operator, &value2); [deskCalc setAccumulator: valuel]; if ( operator == ) [deskCalc add: value2]; else if ( operator == ) [deskCalc subtract: value2]; else if ( operator =='') [deskCalc multiply: value2]; else if ( operator == '/') [deskCalc divide: value2]; NSLog (@"%.2f", [deskCalc accumulator]); [deskCalc release]; [pool drain]; return 0; } Вывод программы 6.8 Type in your expression. (Введите ваше выражение) 123.5 + 59.3 182.80 Вывод программы 6.8 (повторный запуск) Type in your expression. 198.7/26 7.64 Вывод программы 6.8 (повторный запуск) Type in your expression. 89.3 2.5 223.25 При обращении к scant указывается, что должны быть считаны три значения в переменные valuel, operator и value2. Значение типа double можно прочитать с помощью символов формата %lf. Этот формат считывает значения в переменную valuel, которая является первым операндом выражения. Затем считывается оператор. Поскольку оператор представляется символом или 7 ), а не числом, он читается в символьную переменную operator. Символы формата %с указывают, что нужно прочитать с терминала следующий символ. Пробелы внутри строки формата указывают, что при вводе допускается любое число пробелов. Это позволяет при вводе значений отделять операнды от оператора. После считывания двух значений и оператора программа сохраняет первое значение в сумматоре (accumulator) калькулятора. Затем значение operator сравнивается с четырьмя возможными операторами. Если найдено допустимое со- ответствие, калькулятору передается сообщение для выполнения операции. В последней строке NSLog значение accumulator считывается для вывода. После этого выполнение программы завершается. Здесь необходимо сказать несколько слов о законченности программы. Хотя приведенная программа выполняет поставленную задачу, ее нельзя назвать законченной, поскольку в ней не учитываются ошибки пользователя. Например, что произойдет, если пользователь ошибся и ввел ? в качестве имени оператора? Программа просто пройдет через оператор if, и на терминале не появится никаких ошибок, уведомляющих пользователя, что он ввел недопустимое выражение. Еще один неучтенный случай — это ввод пользователем нулевого значения делителя в операции деления. Программа должна проверять этот случай. Определение ситуаций, при которых возможен отказ программы или получение нежелательных результатов, и принятие предупредительных мер — обязательная часть разработки надежных программ. Программа 6.8А является модифицированной версией программы 6.8. В ней учитываются деление на нуль и ввод неизвестного оператора. Программа 6.8А // Программа вычисления простых выражений в форме // число оператор число import
// Здесь нужно вставить секции interface и implementation // для класса Calculator int main (int argc, char argv[]) NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; double value 1, value2; char operator; Calculator deskCalc = [[Calculator alloc] init]; NSLog (@"Type in your expression."); scant ("%lf %c %lf", &value1, &operator, &value2); [deskCalc setAccumulator: valuel]; if ( operator =='+') [deskCalc add: value2]; else if ( operator =='-') [deskCalc subtract: value2]; else if ( operator =='’ ) [deskCalc multiply: value2]; else if ( operator == ' / ' ) if ( value2 == 0 ) NSLog (@"Division by zero."); else [deskCalc divide: value2]; else NSLog (@"Unknown operator."); NSLog (@"%.2f", [deskCalc accumulator]); [deskCalc release]; [pool drain]; return 0; } Вывод программы 6.8A Type in your expression. (Введите ваше выражение) 123.5 + 59.3 182.80 Вывод программы 6.8А (повторный запуск) Type in your expression. 198.7/0 Division by zero. (Деление на нуль) 198.7 Вывод программы 6.8А (повторный запуск) Type in your expression. 125 $28 Unknown operator. (Неизвестный оператор) 125 Если введен оператор деления (слэш), то проверяется, не равно ли нулю значения value2. Если равно, выводится соответствующее сообщение; если нет - выполняется операция деления и выводятся результаты. Обратите внимание, как в данном случае вложены оператор if и предложения else. Предложение else в конце программы позволяет «уловить» любые непредусмотренные случаи. Поэтому любое значение переменной operator, которое не совпадает с одним из четырех символов вычислений, приводит к выполнению предложения else, и на терминал выводится сообщение «Unknown operator» (Неизвестный оператор). Более удобный способ обработки ситуации деления на ноль — это выполнение проверки в методе, который работает с операцией деления. Метод divide: можно модифипировать следующим образом. -(void) divide: (double) value { if (value != 0.0) accumulator /= value; else { NSLog ((@"Division by zero."); accumulator = 99999999.; ) } Для ненулевого значения выполняется деление; для нулевого — выводится сообщ ен и е, и переменной accumulator п ри сваи вается значение 99999999. Это произвольно выбранное значение; можно было присвоить значение 0 или задать специальную переменную, указывающую состояние ошибки. Лучше включать обработку особых ситуаций в сам метод, чем полагаться на предусмотрительность программиста при использовании метода. Оператор switch
Цепочки if-else гак часто используются при разработке программ, что в язык Objcctivc-C включен специальный оператор для выполнения именно этой функции. Это оператор switch. Он имеет следующий формат. switch (выражение) { case valuel: программный оператор программный оператор break; case value2. программный оператор программный оператор break; case valuen: программный оператор программный оператор break; default: программный оператор программный оператор break; } Выражение, указанное в круглых скобках, последовательно сравнивается со значениями valuel, value2, ..., valuer), которые могут быть простыми константами или константными выражениями. Если найден вариант (case), при котором значение равно значению выражения, выполняются программные операторы, следующие после этого предложения case. Если включено несколько таких операторов, то их можно не заключать в фигурные скобки. Оператор break вызывает прекращение выполнения оператора switch. Не забывайте включать оператор break в конце каждого предложения case, иначе выполнение программы продолжится в следующем предложении case. Иногда это делают преднамеренно; в таких случаях обязательно включите комментарии, чтобы предупредить других о ваших целях. Особый (необязательный) вариант с именем default выполняется в том случае, если значение выражения не соответствует ни одному из значений, указанных в предложениях case. Это эквивалентно предложению else, которое использовалось в предыдущем примере для «улавливания» любых неуказанных вариантов. На самом деле общую форму оператора switch можно представить в эквивалентной форме оператора if. if (выражение == value 1) { программный оператор программный оператор } else if (выражение == value2) { программный оператор программный оператор } else if (выражение == valuen) { программный оператор программный оператор > else { программный оператор программный оператор } Мы можем теперь преобразовать большой оператор if из программы 6.8А в эквивалентный оператор switch. Это показано в программе 6.9. Программа 6.9 // Программа вычисления простых выражений в форме // число оператор число import