Скриптовый язык Ruby. Легкий путь в мир программирования. Часть 4

Циклы

При написании программ в большинстве случаев нельзя обойтись без многократного повторения каких-либо действий, пока соблюдается
определенное условие либо просто некоторое количество раз.

В Ruby имеется комплект циклов на все случаи жизни.

Цикл while

Этот способ организации циклов относится к условным. Действия будут повторяться, ПОКА верно указанное в заголовке цикла условие.

a_text = ""

while a_text.length < 20 do a_text = gets end

В этом простейшем примере Ruby будет запрашивать текстовую строку с клавиатуры до тех пор, пока не будет введена строка длиной более 20 символов.

А это – более сложный пример. Скрипт принимает с клавиатуры произвольные текстовые строки, берет их первый символ, и если он является заглавным или строчным латинским символом, добавляет к условной текстовой переменной. Цикл будет продолжаться до тех пор, пока длина этой переменной не достигнет 8 символов.

b_text = ""

while b_text.length < 8

a_text = gets

if (?A..?z) === a_text[0] : b_text += a_text[0].chr end

end

Обратите внимание, при «многострочном» оформлении цикла разделитель do не обязателен.
Как и ветвление, цикл while допускает использование «постфактум».

b_text = ""

begin

a_text = gets

b_text += a_text

end while a_text != "\n"

Данный скрипт принимает с клавиатуры текстовые строки и «складывает» их в текстовую переменную a_text, до тех пор, пока не будет введена пустая строка. Использование оператора цикла «постфактум» позволило отказаться от инициализации переменой a_text. Правда, пришлось добавить служебное слово begin, чтобы отграничить область цикла.

Цикл until

Этот оператор является антиподом while, то есть крутит цикл, ПОКА исходное условие НЕ ВЕРНО. Он не имеет существенных отличий в оформлении от while. Существование его, как и ветвления unless, обусловлено наличием методов, дающих на выходе логическое значение.

Да и изящество программирования тоже важно. Красивый пример цикла until вы уже видели в главе «Первое знакомство».

Разумеется, и этот цикл допускает использование «постфактум».

a = rand(1000) until (500..600) === a

Цикл гоняет функцию генерации случайных чисел до тех пор, пока не выпадет число в потребном промежутке. В данном случае — от 500 до 600. Цикл со счетчиком – for..in

Иногда бывает удобнее просто прокрутить цикл энное количество раз. Для этого существует оператор «цикл со счетчиком».

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

print " |"
for j in 0..15 do print sprintf(" %X ", j) end
puts "", "-"*53
for i in 2..15
print sprintf(" %X | ", i)
for j in 0..15
print((i*16+j).chr, " ")
end
puts
end

Как видим, цикл for можно оформлять одной строкой с использованием разделителя do, либо в более наглядной многострочной манере.

Понятие счетчика цикла в Ruby тесно связано с массивом. Фактически объявляемый в заголовке цикле диапазон преобразуется в массив. Вообще, в качестве счетчика, можно сразу заявить массив.

Предположим, у нас имеется список шрифтов. Как известно, шрифты бывают четырех типов: обычные (regular), полужирные (bold), курсивные (italic) и смешанные (bold-italic).

Пускай, к примеру, он будет таким:

base_txt = <
Arial Regular

Arial Bold

Arial Italic

Aсademy Regular

Aсademy Bold-Italic

Pragmatica Bold

Pragmatica Bold-Italic

Times Regular

Times Bold

Times Italic

Times Bold-Italic

Verdana Regular

Verdana Bold-Italic

t_end

Наша задача – рассортировать шрифты по типам.

Вот этот скрипт успешно решает ее.

out_txt = ""

for i in ["Regular", "Bold", " Italic", "Bold-Italic"]

base_txt.each do |line|

if line[-i.length-1,i.length] == i

out_txt += line

end

end

end

Он поочередно перебирает вышеуказанный текст на предмет наличия очередного атрибута, выводя строки, содержащие его, в отдельный текст. Обратите внимание, как изящно решена проблема недопущения дублирования строк в результирующем тексте. Ведь словосочетание «Bold-Italic» включает в себя и «Bold». Поэтому при сравнении обращение идет лишь к соответствующему фрагменту текста.

Счетчиком может стать даже хэш. Правда, при этом он рассматривается как массив, где каждый элемент – хэш-пара, но тем не менее…

Например, этот скрипт выведет на экран названия животных, которые определены как рыбы.

zoo = {"дятел" => "птица", "лещ" => "рыба",

"удод" => "птица", "окунь" => "рыба"}

for key,value in zoo

if value == "рыба" : puts key end

end

Обратите внимание, что хотя значение счетчика можно (и должно) использовать в теле цикла, переопределить оное значение в означенном теле не представляется возможным.

В качестве иллюстрации создадим скрипт:

for i in (1..16)

puts i

i += 4

puts i

end

Теоретически цикл должен быть выполнен максимум 4 раза. Однако он будет прокручен все 16, причем из выводимой информации будет видно, что значение счетчика изменяется вручную только лишь до начала следующего цикла.

Итераторы

Кроме стандартных циклов со счетчиком в Ruby имеется такое мощное средство, как итераторы.

Итераторы можно разделить на числовые (numeric) и объектные (collection).

Простейший числовой итератор – times. Принимая на входе целое положительное число n, он прокручивает цикл ровно n раз.

К примеру, этот скрипт заполняет массив двенадцатью случайными числами от 1 до 99.

a = []

12.times {|i| a[i] = rand(100)}

Обратите внимание, внутренний счетчик итераций начинается, как положено, с нуля. Соответственно, коррекция номера элемента массива не требуется.

А этот скрипт создает строку из 14-ти заглавных латинских букв. Само собой, случайно выбранных букв. Поскольку здесь используется несколько команд, он оформлен по-другому.

pass = ""

14.times do

a = 0

a = rand(0x80).chr until ("A".."Z")=== a

pass += a

end

Метод times предусматривает использование только одного целого числа. Если же требуется использовать диапазон, для этого существуют другие инструменты.
Метод upto прокручивает цикл со значением счетчика от базового до конечного, с увеличением на единицу.

0xC0.upto(0xFF) { |i| print i.chr }

Здесь внутренний счетчик итераций равен текущему значению из диапазона.
А вот метод downto делает то же самое, но с уменьшением счетчика на единицу. Так сказать, обратный отсчет.

print "Начинаем отсчет:\n "

10.downto(0) do |i|

print "\b"*2,sprintf("%02d",i),"\a"

sleep 0.92 # пауза 0,92 секунды

end

puts "\n\n ПУСК!!!\a\a\a"

И последний метод, step, также прокручивает цикл от начального значения до конечного, но с заданным шагом. Это тем более интересно, так как цикл for изменения шага не предусматривает.

В качестве иллюстрации перепишем пример с выводом кодовой таблицы, заменив циклы for на методы-итераторы.

print " |"

16.times {|j| print sprintf("%X", j)}

puts "", "-"*18

32.step(255,16) do |i|

print sprintf("%X|", i/16)

0.upto(15) {|j| print((i+j).chr)}

puts

end

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

Объектные итераторы в качестве счетчика используют элементы массива. А также все то, что может интерпретироваться как массив. В том числе строки, символы и даже хэш-пары. Таких методов в Ruby очень много.

Как правило, используется обычная связка метод-блок.

Один пример объектного итератора уже был продемонстрирован в программе сортировки названий шрифтов. Вот, если угодно, еще один:

data_m = Array.new(5) { |x| print "Введите #{x}-ое значение массива:"; x = gets.chomp}

Эта строка создает новый массив длиной 5 элементов и тут же заполняет его с клавиатуры.

Обратите внимание, как используется переменная итератора. Вначале она равняется текущему номеру операции, а при присвоении значения уже подразумевается элемент массива.

Если количество операций в блоке превышает две, то для удобства восприятия лучше оформить блок не фигурными скобками, а конструкцией do…end.

data_m = Array.new(7) do |x|

print "Введите #{x}-ое значение массива:"

a = gets.to_f

redo if (a == 0) & !($_[0] == (?0))

x = a

end

Данный скрипт наполняет массив числовыми значениями. Так как используемый метод перевода строки в число не сообщает об ошибке, в случае подачи на вход буквенной строки, добавлена проверка, вызывающая повторный ввод значения.

Управление циклами

Кроме полного ассортимента циклов, Ruby включает в себя целый набор команд управления ими.

Команда break позволяет прервать выполнение цикла.

a_text = ""

while a_text.length < 120 do

a_text += gets

break if $_.chomp == ""

end

В приведенном примере ввод пустой строки завершает цикл, не дожидаясь, пока длина строки-накопителя превысит 120.

Команда next не прерывает цикл, а просто переходит к следующей итерации.

a_text = ""

i = 0

while a_text.length < 30 do

a = gets

next if a.chomp.length < 3

i += 1

a_text += (sprintf("%02d: ",i)+a)

end

Цикл, опять же, собирает вводимые с клавиатуры строки в накопительную переменную, при этом добавляя к ним порядковый номер. Если во введенной строке менее трех символов, такая строка добавлена не будет.

Далее следуют команды redo и retry. Первая перезапускает сначала текущую итерацию, вторая – весь цикл.

a_text = ""

for i in (1..7) do

a = gets

if a.chomp.length < 3

print "\a"

redo

end

if a.chomp == "RET"

print "\a\a"

a_text = ""

retry

end

a_text += (sprintf("%02d: ",i)+a)

end

Программа принимает с клавиатуры семь строк, нумеруя их, и сбрасывая в накопительную переменную. Опять же, отсекая слишком короткие строки. Введенное же слово RET воспринимается как команда полного перезапуска цикла. При этом подается звуковой сигнал и производится очистка переменной-накопителя.

Все эти нюансы дают программисту дополнительные возможности при оформлении циклов.

Бесконечный цикл - loop

Наличие столь продвинутых инструментов управления делает логичным и целесообразным существование цикла, выход из которого возможен лишь при помощи команды прерывания.

a_text = ""

loop do

a_text += gets

break if $_.chomp == ""

end

Скрипт собирает строки с клавиатуры в текстовую переменную до тех пор, пока не будет введена пустая строка.

Юзич yuzich17@mail.ru


Компьютерная газета. Статья была опубликована в номере 30 за 2010 год в рубрике программирование

©1997-2024 Компьютерная газета