Шарп. Тема 6. Циклы. Цикл for что есть что в цикле
Скачать 0.54 Mb.
|
Цикл for: что есть что в цикле Отвлечёмся от повседневных дел и продолжим погружаться в азы C#. В этой теме вас ждёт знакомство с циклами. Они позволяют выполнять одно и то же действие несколько раз. К примеру, представим, вы завели хомяка. Грызуна зовут Байт, он любит есть морковки. Каждый день Байт съедает три морковки. Отразить это в коде можно так: Console.WriteLine("Байт съел морковку."); Console.WriteLine("Байт съел морковку."); Console.WriteLine("Байт съел морковку."); Отлично, всё работает, но что если Байт начнёт съедать в день по 10 морковок? Можно повторить строку кода нужное число раз, но тогда программа станет громоздкой. Разработчики вообще не любят дублировать код — это скучно и неудобно. Есть даже профессиональное правило DRY — Don't Repeat Yourself(англ. «не повторяй себя»). Поэтому в C# (и в других языках тоже) используются циклы — с их помощью действие выполняется нужное количество раз, но код при этом не дублируется. Существует несколько типов циклов — они начинаются с разных служебных слов и применяются для разных задач. Сначала мы подробно разберём цикл, который начинается со служебного слова for. Он используется тогда, когда число повторений известно заранее, — к примеру, сейчас мы знаем, что Байту нужно 10 морковок. Когда вы определили число итераций — повторений, которые должен выполнить цикл, нужно его настроить. После слова for в круглых скобках требуется задать стартовое значение цикла, условие его работы и порядок выполнения этого условия. От этих трёх параметров зависит, откуда цикл начнёт свое движение, сколько раз выполнится и как будет меняться его начальное значение. После этого в фигурных скобках указывается тот участок кода, который нужно повторять, — он называется телом цикла. Только после того, как цикл выполнит все итерации, заложенные в его условии, программа начнёт выполнять код, идущий следом. Всё вместе это выглядит так: for (int i = 1; i <= 10; i = i + 1) { // Объявление цикла и его настройки Console.WriteLine("Байт съел морковку."); // Тело цикла, выполняется при каждой итерации } // Цикл завершён, выполнится эта строка: Console.WriteLine("Все морковки съедены. Морковок больше нет."); Сначала мы объявили переменную итерирования: с помощью неё настраивается количество повторов в цикле. Имя переменной вы выбираете сами, но традиционно её называют i — как и в нашем примере. Начальное значение равно единице (int i = 1) — именно с этой цифры ведётся отсчёт цикла. Далее идёт условие, при котором цикл будет работать. Так как Байт съест 10 морковок, условие такое: i <= 10. Пока оно истинно — цикл выполняется, а значение переменной i пошагово изменяется. Когда условие задано, нужно отразить в настройках цикла, что за один подход хомяк съедает одну морковку. Это выглядит так: i = i + 1 — значение переменной i при каждой итерации должно возрастать на единицу. В теле цикла укажем текст, который нужно повторять — Console.WriteLine("Байт съел морковку.");. Когда все итерации цикла будут выполнены, появится сообщение о том, что морковки съедены. Результат запуска кода будет таким: Байт съел морковку. Байт съел морковку. Байт съел морковку. Байт съел морковку. Байт съел морковку. Байт съел морковку. Байт съел морковку. Байт съел морковку. Байт съел морковку. Байт съел морковку. Все морковки съедены. Морковок больше нет. Если требуется увеличить или уменьшить количество итераций, нужно изменить условие цикла. К примеру, десять морковок для Байта много — он сильно прибавил в весе. Снизим число морковок до пяти в день. В новом условии нужно отразить, что переменная итерирования i должна быть меньше или равна 5. Для контроля съеденного запишем все морковки. Для этого также можно использовать переменную итерирования — она доступна внутри цикла не только в его настройках, но и в теле (при этом не видна за пределами цикла). В примере с Байтом значение переменной будет меняться при каждой итерации в последовательности от 1 до 5 — если поставить её в тело цикла, то будет учтена каждая морковка. Код получится таким: for (int i = 1; i <= 5; i = i + 1) { Console.WriteLine("Байт съел " + i + "-ю морковку."); } Результат Байт съел 1-ю морковку. Байт съел 2-ю морковку. Байт съел 3-ю морковку. Байт съел 4-ю морковку. Байт съел 5-ю морковку. Во время выполнения цикла происходит следующее: при каждой итерации значение i увеличивается на единицу, после чего программа проверяет заданное условие (i <= 5) на истинность. Цикл повторяется до тех пор, пока условие истинно. Если Байт захочет съесть 6-ю морковку, C# определит, что 6 больше 5 — условие ложно, и цикл завершится. Измените условие цикла (к примеру, i <= 3 ) и убедитесь, что Байт не сможет съесть больше морковок, чем вы укажете. Обратите внимание, в примере с Байтом и морковками мы использовали знак нестрогого сравнения: <= (меньше или равно). Задать условие, что в цикле должно быть пять повторений, можно и по-другому, с помощью знака строгого сравнения : i < 6. Выражения i <= 5 и i < 6 полностью равнозначны — можно использовать их оба. Переменная итерирования может принимать не только положительные значения или равняться нулю, но и быть отрицательной. Предположим, вы с Байтом решили переехать в новую квартиру и в вашем доме теперь есть подземная парковка (минус первый этаж) и даже лобби (нулевой этаж). Ваша квартира находится на третьем этаже, и вам нужно подняться в неё на лифте с паркинга. В таком случае запрограммировать табло лифта можно так: for (int i = -1; i <= 3 ; i = i + 1) { Console.WriteLine("Этаж " + i); } На экране будет напечатано: Этаж -1 Этаж 0 Этаж 1 Этаж 2 Этаж 3 Цикл for: движение в обратном направлении и изменение шага Продолжаем. Весь прошлый урок вы работали с циклами, где переменная итерирования увеличивалась — морковки, овечки и этажи прибавлялись. Теперь представим, что вам нужно запрограммировать цикл for в обратном порядке. К примеру, на лифте можно не только подниматься, но и спускаться — номера этажей в таком случае отображаются по убыванию. Код цикла для лифта, который спускается с девятого на первый этаж, получится таким: for (int i = 9; i > 0 ; i = i - 1) { Console.WriteLine("Этаж " + i); } Начальное значение переменной итерирования i равно 9 — лифт начинает движение вниз с девятого этажа. Поскольку цикл должен завершиться, когда лифт доедет до первого этажа, в условии значение переменной i должно быть больше ноля либо больше или равно единице: i > 0 или i >= 1. Номера этажей уменьшаются, поэтому при каждой итерации требуется не прибавлять к i единицу, а отнимать — получится i = i - 1. Результатом выполнения такого цикла будет: Этаж 9 Этаж 8 Этаж 7 Этаж 6 Этаж 5 Этаж 4 Этаж 3 Этаж 2 Этаж 1 Запрограммировать цикл в обратном порядке не сложнее, чем в прямом. Кстати, для уменьшения переменной на единицу i = i - 1 можно использовать более простую запись: i-- — чем меньше кода, тем легче он читается и воспринимается. Операции i = i - 1 и i-- равнозначны. Для увеличения переменной i на единицу можно также использовать сокращённое выражение i++. На месте i может быть любая другая переменная. Отметьте циклы, идущие в обратном порядке: 1.for (int i = 0; i <= 100; i = i + 1) 2.for (int i = 7; i > 1; i--) 3. for (int i = 12; i > 0; i++) 4. for (int k = 3; k > 0 ; k = k - 1) Переменная итерирования может меняться не только на единицу. Представим, что лифт перепроектировали. Теперь он останавливается только на нечётных этажах. Появилась новая последовательность: вместо 1, 2, 3, 4, 5... стало 1, 3, 5, 7, 9. Чтобы запрограммировать такой лифт, нужно изменить шаг цикла — число, на которое после каждой итерации изменяется его переменная. Раньше был шаг 1, а теперь будет 2. Заменим в коде операцию i = i + 1 на i = i + 2: for (int i = 1; i <= 9 ; i = i + 2) { Console.WriteLine("Этаж " + i); } Теперь лифт будет открывать двери только на первом, третьем, пятом, седьмом и девятом этажах. Мы можем сделать и так, чтобы он останавливался через каждые пять этажей (i = i + 5) — шаг цикла может быть любым числом. Его можно как увеличивать, так и уменьшать. Как должен выглядеть цикл, чтобы лифт двигался по чётным этажам сверху вниз? 1. for ( int i = 9; i >= 1 ; i = i + 2 ) 2. for ( int i = 8; i >= 1 ; i = i - 1 ) 3. for ( int i = 8; i >= 1 ; i = i - 2 ) 4. for ( int i = 8; i >= 1 ; i = 2 ) Пока проектировали лифты, совсем забыли о Байте, а он тем временем съел всю морковь, и теперь вам нужно идти в магазин. До этого момента мы использовали цикл, чтобы напечатать строку нужное число раз. Однако его также можно использовать для расчётов. К примеру, вы хотите понимать, сколько морковок нужно покупать для Байта в течение недели с учётом того, что он съедает по пять в день. С помощью цикла можно вывести на экран, сколько овощей съедает грызун в течение этих небольших сроков — от одного до семи дней. Заодно напечатаем общий рацион Байта на неделю. Вот что получится: using System; namespace prog1 { public class Program1 { public static void Main(string[] args) { int carrotCount = 0; // Объявляем переменную для общего числа морковок int carrotPerDay = 5; // В этой переменной фиксируем ежедневный рацион // Число итераций совпадает с количеством дней в неделе for (int day = 1; day <= 7; day = day + 1) { // Переменная итерирования - day carrotCount = carrotCount + carrotPerDay; // При каждой итерации плюс 5 морковок // Сколько морковок Байт съедает за разное количество дней Console.WriteLine(day + "-й день, Байт съест " + carrotCount + " морковок."); } // Сколько овощей требуется покупать на неделю Console.WriteLine("Рацион на неделю: " + carrotCount + " морковок."); } } } Результат 1-й день, Байт съест 5 морковок. 2-й день, Байт съест 10 морковок. 3-й день, Байт съест 15 морковок. 4-й день, Байт съест 20 морковок. 5-й день, Байт съест 25 морковок. 6-й день, Байт съест 30 морковок. 7-й день, Байт съест 35 морковок. Рацион на неделю: 35 морковок. Мы объявили переменную для общего количества моркови carrotCount и переменную для ежедневного рациона Байта carrotPerDay. Затем мы запустили цикл на семь итераций (неделю), в ходе которого считаем съеденные хомяком овощи — каждый день прибавляется по пять морковок. Всего на неделю нужно 35 морковок. Чтобы такого количества моркови хватило, например, на две недели, ежедневный рацион должен быть другим. Измените в коде начальные значения переменных— число дней и количество морковок — и посмотрите, сколько корнеплодов съедает Байт за разные промежутки времени. Условия задачи можно усложнить. К примеру, сосед привёз для Байта с дачи немного моркови — теперь у вас есть небольшой запас. Вы хотите посчитать, сколько нужно докупить, чтобы хватило на месяц. Для решения этой задачи добавим в код ещё одну переменную. И вжух: using System; namespace prog1 { public class Program1 { public static void Main(string[] args) { int carrotCount = 0; // Переменная для подсчёта общего количества морковок int carrotPerDay = 3; // Ежедневный рацион теперь три морковки int carrotReserve = 23; // Новая переменная! она отражает, что у вас уже есть 23 морковки // цикл на месяц for (int day = 1; day <= 31; day = day + 1) { carrotCount = carrotCount + carrotPerDay; // Вычисляем рацион } // Вычитаем из общего рациона число морковок, которые есть в запасе Console.WriteLine("Нужно докупить моркови:" + (carrotCount - carrotReserve)); } } } Результат Нужно докупить моркови:70 В новой переменной carrotReserve хранится число морковок, которые есть у вас дома. В цикле считается, сколько их нужно на месяц — это значение сохранится в переменной carrotCount. После выполнения цикла из общего числа морковок (carrotCount) вычитается их запас (carrotReserve). Если вы измените в коде значения переменных carrotPerDay и carrotReserve, то узнаете, насколько могут быть прожорливы хомяки. Экспериментируйте! Поздравляем! Плюс ещё один урок в вашу копилку знаний о циклах! Теперь вы умеете настраивать в цикле обратный порядок и менять его шаг. Также вы понимаете, как модифицировать циклы для вычислений. Немного потренируемся. Циклы с условием и вложенные циклы Вы уже немало знаете о циклах — умеете с их помощью печатать строки и проводить расчёты. Однако циклы не существуют в программе сами по себе. Их выполнение может зависеть от внешних обстоятельств. К примеру, благодаря советам ветеринара Байт отлично похудел к лету. Чтобы оставаться в форме, он решил устраивать разгрузочные дни. Каждое утро Байт взвешивается и определяет, будет ли он сегодня есть морковки или только попьёт воды и побегает в колесе. Если вес меньше 800 грамм, то Байт съедает как обычно пять морковок, в обратном случае объявляет разгрузочный день. Чтобы отразить это в коде, нужно добавить к циклу условное выражение: using System; namespace prog1 { public class Program1 { public static void Main(string[] args) { int weight = 750; // Объявили переменную веса Байта и присвоили ей значение if (weight < 800) { // Цикл сработает, только если вес Байта меньше 800 грамм for (int i = 1; i < 6; i = i + 1) { Console.WriteLine("Байт съел " + i + "-ю морковку"); } } else { Console.WriteLine("Разгрузочный день. Пьём водичку, крутим колесо!"); } } } } Результат Байт съел 1-ю морковку Байт съел 2-ю морковку Байт съел 3-ю морковку Байт съел 4-ю морковку Байт съел 5-ю морковку Ура! Цикл с поеданием морковок запустится, только если вес Байта меньше 800 грамм. Поменяйте вес хомяка и позапускайте код — проверьте, что Байт точно не потолстеет! Условные выражения и циклы часто используются вместе. Причём и цикл может находиться внутри условного выражения, как в примере с Байтом, так и условное выражение может быть внутри цикла. Прочитайте этот код исходя из того, что вы уже знаете о циклах и условных выражениях. Каким будет конечное значение переменной counter? using System; namespace prog1 { public class Program1 { public static void Main(string[] args) { int counter = 0; for (int i = 0; i < 10; i = i + 1) { if (i > 7) { counter = counter + 2; } counter = counter + 1; } } } } Цикл while Уф! Сколько всего вы уже знаете о циклах! Так держать — ещё пара уроков и вы станете настоящим профи в этой теме — краеугольной для любого разработчика. На прошлых уроках мы много говорили о цикле for — он подходит для тех случаев, когда заранее известно количество итераций. Но так бывает не всегда. К примеру, вы закупили для Байта корм впрок — неизвестно, на сколько порций его хватит. В этом случае работа цикла будет определяться не числом повторений, а булевым выражением — питомец ест корм до тех пор, пока он в наличии. Если корм кончился — булево выражение стало ложным — цикл завершится. Такой цикл описывается с помощью служебного слова while (от англ. while — до тех пор, пока). В цикле for мы определяли переменную итерирования, условие и шаг цикла. В цикле while нам неизвестно число итераций, поэтому не получится задать ни переменную, ни шаг. Мы знаем только условие его работы. Его и указываем после слова while, перед телом цикла в фигурных скобках: int foodWeight = 500; // Количество корма while (foodWeight >= 10) { // Условие работы цикла - пока осталась хотя бы одна порция foodWeight = foodWeight - 10; // Байт съедает 10 грамм корма за раз } Console.WriteLine("Корм закончился! Пора идти в магазин!"); Структура циклов for и while похожа, как и логика их работы — цикл выполняется, пока условие верно. В нашем примере цикл while будет работать до тех пор, пока значение переменной foodWeight больше или равно 10 — дома есть хотя бы одна порция корма. Как только переменная станет меньше 10 — цикл завершится. После этого будет выполнен следующий код — напечатано предупреждение, что пора идти в магазин. Рассмотрим пример. Вам нужно запрограммировать робота для перемещения коробок на складе. За один подход можно взять одну коробку, при этом их общее количество каждый раз разное. Какой цикл подойдёт для решения такой задачи? 1. Цикл for c условным выражением 2. Цикл while 3. Цикл for 4. Вложенный цикл for Бесконечный цикл Продолжаем изучать возможности цикла while. Он выполняется до тех пор, пока условие является истинным. Следовательно, если в условии написать true, то цикл будет выполняться снова и снова. Посмотрим, что получится, если запустить такой цикл для пробежки Байта в колесе. while (true) { Console.WriteLine("Бегу, бегу, бегу!"); // Будет выполняться бесконечно } Console.WriteLine("Отлично побегал!"); // Эта строка не будет напечатана В консоли будет беспрерывно печататься строка "Бегу, бегу, бегу!", а до строки "Отлично побегал!" очередь никогда не дойдёт. Хомяк Байт в колесе превратился в вечный двигатель! Такой цикл, который начинается со служебного слова while и содержит в условии true, называется бесконечным. И никакого символа бесконечности ∞ не требуется — его просто-напросто нет в синтаксисе C#.\ Если же написать в условии цикла false и попробовать запустить код, то вообще ничего не получится. Программа выдаст ошибку: unreachable statement (англ. «недостижимое утверждение»). while (false) { Console.WriteLine("Никуда не побегу!"); // Такой цикл не запустится. Хомяк решил поспать. } Бесконечные циклы активно используются, например, при передаче данных, когда их объём неизвестен заранее. Или в построении web-приложений, которые общаются через не всегда надёжную сеть и могут «падать», и поэтому им требуется снова и снова отправлять друг другу запросы. При работе с пользователями бесконечные циклы нужны, чтобы обеспечить беспрерывное ожидание и обработку таких операций, как касание экрана, свайпы или ввод текста и других. Однако Байт не хочет вечно бегать в колесе — вам нужно закончить цикл. Выйти из бесконечного цикла можно двумя способами, первый — остановить программу. Но это всё равно что сломать колесо — не очень практично. Более рационально использовать второй способ — добавить в код слово break (англ. «перерыв»). На break цикл сразу завершится, а программа перейдёт к выполнению следующего кода. Байт сможет закончить пробежку и пойти перекусить. Это выглядит так: while (true) { Console.WriteLine("Бегу, бегу, бегу!"); break; // Цикл завершился } Console.WriteLine("Отлично побегал, пойду поем!"); // Теперь эта строка будет напечатана В результате будет напечатано: Бегу, бегу, бегу! Отлично побегал, пойду поем! Бесконечный цикл с прерыванием break нужен, когда требуется выполнять условно неограниченное количество итераций до достижения определённого результата. Например, при вызове такси в приложении радиус поиска свободной машины расширяется до тех пор, пока не найдётся первый подходящий автомобиль. Однако условий выхода из цикла (подходящих машин) может быть несколько, и в таком случае мы можем предложить пользователю выбор. Как работает бесконечный цикл с break с несколькими условиями выхода, разберём на примере первого тренажёра этого урока — в нём специально нет правильных ответов. Переходите к нему! Прокачиваем финансовое приложение и добавляем в него циклы Пришло время усовершенствовать финансовое приложение. Сейчас оно работает так: предлагает ввести баланс на счету в рублях и число дней до зарплаты, после чего просит выбрать команду convert или advice. В зависимости от команды программа либо конвертирует деньги в какую-то из доступных валют — доллары, евро или йены, либо дает совет, где сегодня поужинать. Код выглядит так: using System; namespace prog1 { public class Program1 { public static void Main(string[] args) { double rateUSD = 78; double rateEUR = 85; double rateJPY = 0.74; Console.WriteLine("Сколько денег у вас осталось до зарплаты?"); double moneyBeforeSalary = double.Parse(Console.ReadLine()); Console.WriteLine("Сколько дней до зарплаты?"); int daysBeforeSalary = int.Parse(Console.ReadLine()); Console.WriteLine("Введите команду. Доступные команды: convert и advice."); String command = Console.ReadLine(); if (command.Equals("convert")) { Console.WriteLine("В какую валюту хотите конвертировать рубли? Доступные варианты: USD, EUR, JPY."); String currency = Console.ReadLine(); if (currency.Equals("USD")) { Console.WriteLine("Ваши сбережения в долларах: " + moneyBeforeSalary / rateUSD); } else if (currency.Equals("EUR")) { Console.WriteLine("Ваши сбережения в евро: " + moneyBeforeSalary / rateEUR); } else if (currency.Equals("JPY")) { Console.WriteLine("Ваши сбережения в йенах: " + moneyBeforeSalary / rateJPY); } else { Console.WriteLine("Валюта не поддерживается."); } } else if (command.Equals("advice")) { if (moneyBeforeSalary < 3000) { Console.WriteLine("Сегодня лучше поесть дома. Экономьте и вы дотянете до зарплаты!"); } else if (moneyBeforeSalary < 10000) { if (daysBeforeSalary < 10) { Console.WriteLine("Окей, пора в Макдак!"); } else { Console.WriteLine("Сегодня лучше поесть дома. Экономьте и вы дотянете до зарплаты!"); } } else if (moneyBeforeSalary < 30000) { if (daysBeforeSalary < 10) { Console.WriteLine("Неплохо! Прикупите долларов и зайдите поужинать в классное место. :)"); } else { Console.WriteLine("Окей, пора в Макдак!"); } } else { if (daysBeforeSalary < 10) { Console.WriteLine("Отлично! Заказывайте крабов!"); } else { Console.WriteLine("Неплохо! Прикупите долларов и зайдите поужинать в классное место. :)"); } } } else { Console.WriteLine("Извините, такой команды пока нет."); } } } } Основной недостаток, который бросается в глаза, — вводить команды словами неудобно, можно легко ошибиться. Нужно исправить код приложения таким образом, чтобы вместо слов он принимал числа. Конвертация валют должна срабатывать, если пользователь ввёл число 1, а выдача совета по поводу ужина — число 2. Для этого нам нужно внести изменения в следующие строки: Console.WriteLine("Введите команду. Доступные команды: convert и advice."); String command = Console.ReadLine(); if (command.Equals("convert")) { ... } else if (command.Equals("advice")) { ... } Для начала сделаем приложение более дружелюбным к пользователю: заменим призыв «Введите команду» на вопрос «Что вы хотите сделать?». Теперь с ним гораздо приятнее иметь дело. Далее создадим и оформим цифровое меню с двумя опциями: «1 — Конвертировать валюту» и «2 — Получить совет». Console.WriteLine("Что вы хотите сделать?"); Console.WriteLine("1 - Конвертировать валюту"); Console.WriteLine("2 - Получить совет"); Теперь программа должна считать ответ пользователя из консоли. Раньше это происходило с помощью команды Console.ReadLine();, однако теперь у нас не слова, а цифры, поэтому нужно использовать int.Parse(Console.ReadLine(). int command = int.Parse(Console.ReadLine() Следующий шаг — отредактировать условия при выборе команд. Вместо слов здесь также будут цифры 1 или 2. Их нужно сравнить с переменной command при помощи оператора проверки на равенство ==. if (command == 1) { ... } else if (command == 2) { ... } Готово! Теперь у финансового приложения появилось цифровое меню, пользователю больше не нужно мучиться, набирая слова на клавиатуре. У финансового приложения теперь есть цифровое меню и вас больше не напугать исключениями. Однако программа по-прежнему даёт выбрать только одну команду, а после её выполнения завершается. Это неудобно — возможно, пользователь захочет не только конвертировать деньги, но и получить совет, однако для этого ему придётся перезапускать приложение. Исправить это можно, если добавить в код бесконечный цикл while и предусмотреть возможность выхода из него, например при выборе цифры 0. Программа сможет работать так: ... Что вы хотите сделать? 1 - Конвертировать валюту 2 - Получить совет 0 - Выход > 2 Окей, сегодня в Макдак!! Что вы хотите сделать? 1 - Конвертировать валюту 2 - Получить совет 0 - Выход > 0 Выход На практике вам нужно завершить «прокачку» финансового приложения — добавить цифровое меню для выбора валюты при конвертации и с помощью бесконечного цикла сделать программу удобной для пользователя. |