Урок Ruby №6: «Автоматизируем повтор кода с помощью циклов»
О циклах я подробно рассказывал в уроке 6 по C++. Там вы можете посмотреть блок-схемы и описание. Тем не менее, чтобы раскрыть данную тему, напишу немного о циклах.
Итак, цикл избавляет программиста от рутинного однообразия. Фактически, это автоматизация выполнения определенного участка кода, пока условие цикла будет равно истине. Представьте себе ситуацию, когда вам нужно будет организовать некий счетчик в вашей программе. Вы выделяете переменную, присваиваете ей начальное значение и начинаете наращивать ее в коде.
Иными словами, у вас будет что-то подобное:
k=1 какое-то выражение k+=1
Вроде бы вы с задачей справились. Однако что, если вам сказали, что нужно будет создать некий список из 10000 тысяч чисел, вычисленных в результате какого-то выражения. Вы что, будете приведенный выше участок кода писать десять тысяч раз? Конечно нет. Ведь для этого есть циклы.
Цикл позволяет нам организовать повтор выполнения кода, который мы можем прекратить, определив некоторое условие для выхода из цикла. Ключевым здесь является слово «может», так как бывает ситуации, когда цикл не может найти условия для выхода. Такой цикл называют бесконечным.
Давайте используем цикл в нашей задаче. Нам известно, что записей должно быть 10000. Что ж, нам же проще. Значение 10000 и будет условием выхода из цикла. Учтите, я приведу сейчас пример цикла с предусловием, тогда как в данном случае гораздо лучше использовать цикл с параметром.
k=1 while kвыражение end
На функцию print пока не обращайте внимание. Она выполняет тоже действие, что и puts –выводит сообщение на экран. Обратите внимание на конструкцию. Цикл начинается с ключевого слова while (пока), затем ставится условие выхода из цикла и ключевое слово do (делай). Цикл заключается в операторные скобки с обязательным проставлением слова end для закрытия этих операторных скобок (глупое наследие Бэйсика). Мы выводим на экран значение выражения и наращиваем переменную k. Если ее убрать, то цикл станет бесконечным, так как условие для выхода из цикла никогда не выполнится.
Кстати, я недаром упомянул слово «условие». Работа с циклом чем-то схожа с работой с условием. Если условие равен истине, то условие выполняется, ложь – цикл прекращает свою работу и программа выполняет код, расположенный за циклом.
Я уже упомянул, что цикл в примере называется циклом с предусловием. Называется он так потому, что условие ставится в начале цикла. Фактически, это очень мудрое решение, так как цикл может быть и не выполнен ни разу. Допустим, ваша программа нарастила какое-то значение и вы поставили условие в цикле для обсчета какого-то выражения. Но условие будет ложным и цикл не выполнится. Такое бывает сплошь и рядом.
Синтаксис цикла с предусловием такой:
while условие do
выражения
end
Причем слово do можно опустить, но я не рекомендую этого делать. Хотя, если вы решили изучать Ruby после Python, подобный синтаксис вам будет более удобен.
Как вы уже поняли, цикл с предусловием может ни разу не выполнится. Однако иногда встречается ситуация, когда цикл должен выполнится хотя бы раз. В этом случае используется цикл с постусловием. Синтаксис этого цикла такой:
begin
тело цикла
end while условие
Как вы заметили, условие выхода из цикла ставится в конце, за что он и получил свое название цикла с постусловием. Давайте рассмотрим предыдущий пример с использованием этого цикла:
k=1 begin print наша формула k+=1 end while k<=10000
Хотелось бы обратить ваше внимание на то, что данный вид цикла не так распространен, как цикл с предусловием. Однако есть ряд задач, которые удобно решить именно с помощью цикла с постусловием. Какой из них где применять вы поймете с практикой.
Кроме того, грех не провести вновь аналогию с Perl. Вы видели, что с if в Ruby можно делать некоторые поблажки в синтаксисе. С while ситуация аналогична:
salary=8000 salary +=500, while salary <=30000
Этот скрипт демонстрирует, как на собеседовании можно выдавливать нужную вам зарплату. Стартовая зарплата работодателя 8000, в цикле мы ее подымаем до 30000. Хотя такой синтаксис и считается нормой, однако я бы предостерег от его использования, так как он затрудняет понимание кода.
Еще одно наследство Perl – цикл until ( выполняй до тех пор, пока условие не будет равно). Это тоже цикл с постусловием. Если вы программировали раньше на Паскале или Бэйсике, то он вам будет знаком. Фактически, этот тот же begin end while, только в этом цикле нужно ставить условие, при котором цикл завершится, когда достигнет этого условия. Сравните:
salary=8000 begin salary+=500 end until salary == 30000
Этот цикл практически не распространен в Ruby, пользуются им , в основном, те, кто перешел на Ruby из Паскаля. Тем не менее, вы спокойно можете его использовать.
Помните первый пример, когда нам нужно было вывести список из 10000 значений? Мы его вывели посредством оператора while. Однако у нас возникло маленькое неудобство в лице переменной k, которую нужно было нарастить самому. Конечно, с одной стороны это более гибкий подход, так как я мог бы сделать шаг цикла равным двум (выводить на экран каждую вторую запись). Но в Ruby есть еще такие объекты как массивы и хэши, элементы которых часто приходится перебирать в цикле.
И в том и в другом случае у нас известно одно –сколько раз цикл должен выполниться. Иными словами, мы знаем параметр, при достижении которого цикл прекратит свою работу. В приведенном примере параметром будет k. Однако есть более изящный способ организовать работу – использовать цикл с параметром.
Цикл с параметром
Цикл с параметром используется только тогда, когда мы точно знаем сколько раз должен выполниться цикл. В С можно было в таком цикле побеспредельничать, однако я не сторонник подобного подхода. Паскалевская реализация цикла с параметром все же отразила на меня какое-то влияние.
Итак, приготовьтесь заполнить вам мозг массой информации. Надеюсь, что у вас не произойдет переполнения буфера J. Реализаций цикла с параметром в Ruby очень много. Одни из них классические и похожи на те, что используются в других языках, другие очень удобны и характерны для Ruby.
Если вы консерватор в программировании типа меня, то вам подойдет цикл for. Это цикл с параметром, где указывается диапазон. Он достался Ruby от python. Синтаксис его довольно интересен:
for параметр in нижняя граница_диапазона ..верхняя граница диапазона do
тело цикла
end
В качестве нижней границы дипазона вы вводите число, от которого начинается отсчет, верхней границей указывается, где он прекратится. Давайте теперь нашу задачу с записями решим, как нужно:
for k in 1..10000 do выражение end
Видите, насколько просто? О переменной k можно вообще забыть, за нас заботу о ее приращении сделает цикл. Таким циклом удобно перебирать коллекции типа массивов. Кроме того, старайтесь соблюдать негласное правило программирования – в качестве переменной параметра ставить символ i (от iterator –перечислитель). Итерация – это умное название выполнения очередного шага цикла, поэтому не беспокойтесь.
Опять же do можно отбросить за ненадобностью, однако end должно стоять обязательно. Кроме того, данный цикл можно записать в одну строчку:
Следующий цикл с параметром в Ruby называется times. Это метод объекта Integer (числа). Пока мы не изучили с вами ООП, но важно понимать, что в Ruby буквально все является объектами. Это касается и чисел. Вот пример:
10000.times {выражение}
Видите, как удобно? Однако запомните – этот метод используется только для целых чисел. Так что не стоит ругаться, когда ваш цикл не сработает в случае его нецелевого использования.
Еще одно фича языка – метод upto. Он используется для объектов типа Integer (число), String (строка) и Date (дата). Работает он как for, однако производительность у него немного быстрее. Кроме того, он является едва ли не стандартом в Ruby. Тем не менее, вы вольны использовать и стандартный for. В этом и заключается свобода языка!
Давайте посмотрим на примере, как он работает:
1.upto (10000) { вывод на экран нашего выражения}
Если в upto отсчет идет на увеличение, то в методе downto он идет на уменьшение. Это реализация цикла бывает полезной, например при переборе массива. Давайте выведем на экран 5 чисел:
5.downto (1) {|i| print I, “”}
Запустите и посмотрите на результат.
Цикл loop
Вы когда-нибудь слышали о goto? Этот оператор был обруган еще в 60-е годы прошлого века, но до сих пор встречается. Это обыкновенная метка, с помощью которой можно реализовать цикл.
В Ruby он перешел под названием loop (петля). По сути, это тот же goto, вот только здесь он вполне легален. Однако не стоит забывать, что loop –это бесконечный цикл, выходить из которого нам нужно самим.
И вот тут на арену выходят операторы управления ходом выполнения цикла. Их всего три – break, redo и retry.
break позволяет прервать выполнение цикла и вызвать досрочный выход из него. Давайте рассмотрим это на примере цикла loop:
k=1 loop do if k>=10000 then break end выражение k+=1 end
Как видите, мы выходим из цикла, как только k=10000. break применяется во всех рассмотренных нами циклах.
redo и retry заставляют цикл выполняться по новой. Кроме того, существует еще ключевое слово next , схожее с continue в других языках. Он прерывает текущее выполнение итерации и запускает следующую итерацию.
Ну и в завершении хотелось бы сказать, что циклы могут быть вложенными. Вот тогда операторы управления ходом цикла будут особенно полезны. Давайте рассмотрим пример таблицы умножения, который и демонстрирует вложенность циклов:
for i in 1..10 do for j in 1..10 do print i.to_s + “x” + j.to_s + “=”, i*j, “\n” end end
Обязательно наберите этот код и запустите на выполнение. Вам важно понять как он работает. Если вас смущает точка после переменных i и j – поясню. Эти переменные являются объектами со своими методами, доступ к которым осуществляется через точку. метод to_s вытаскивает текущее значение переменной. Пока вам может быть непонятно, что такое объект или метод. Скоро мы разберем с вами тему объектно-ориентированного программирования более подробно. На этом урок о циклах можно считать законченным.