Скриптовый язык Ruby. Легкий путь в мир программирования. Часть 4
Организация ветвлений
В программировании обыденны ситуации, когда нужно пропустить выполнение какого-либо фрагмента программы, перейдя сразу к следующему. Такие ситуации называются ветвлением.
К сожалению (или, вернее, к счастью!), в Ruby нет оператора безусловного перехода, наподобие goto МЕТКА. По причине отсутствия в этом языке всякого присутствия такого понятия, как «метка».
Все операторы ветвления подразумевают использование как минимум одного логического значения, которое может быть истинным (true) или ложным (false).
Логическое значение же, как правило, является результатом операции сравнения или тестирования. Давайте рассмотрим их.
Операции сравнения
Одно из наиболее частых сравнений – это проверка на равенство, или эквивалентность.
Для иллюстрации создадим скрипт.
a, b = 7, 5
l = a == b
puts l
Из этого текста можно уяснить следующее: во-первых, оператор «равенство» – это «==» (в отличие от «=» - оператора присваивания) и, во- вторых – результат операции сравнения можно назначить переменной.
Вообще, результатом операции сравнения является логическая, она же булева, величина, которая может принимать два разных значения – true (истинно) или false (ложно).
В частности, запустив этот скрипт, мы получим закономерное сообщение:
false
А заменив
a == b
на
a != b
получим, соответственно
true
То есть «!=» является оператором «не равно».
Продолжим эксперименты. Заменим в нашем скрипте две последние строки на
l = !(a == b)
puts !l
Запустив скрипт, получим результат:
false
Проведем разбор полетов. Оператор «!» есть логическое отрицание (обращение булева значения на противоположное). То есть результат сравнения, заключенного в скобки, был обращен (false превратилось в true) и присвоен переменной. А при выводе на экран - еще раз обращен. Из вышеприведенного примера следует, что если мы хотим обратить значение операции сравнения, оную операцию надобно заключать в скобки. Иначе транслятор истолкует оператор «!» как обращение первого значения.
Кстати, оператор обращения действует и на переменные с не логическим значением. А поскольку таковые значения приравнены к значению «true», результатом операции будет «false».
Кроме операторов «равно»-«не равно» в Ruby существует полный комплект операторов сравнения «больше»-«меньше»
a > b # a БОЛЬШЕ b
a < b # a МЕНЬШЕ b
a >= b # a БОЛЬШЕ либо РАВНО b
a <= b # a МЕНЬШЕ либо РАВНО b
А также вот такой универсальный оператор:
a <=> b
Результатом его работы будет не логическое значение, но целочисленное: -1 - если а меньше b; 0 – если a равно b; 1 – если a больше b. Так, со сравнением мы худо-бедно разобрались. Но в Ruby еще имеются такие интересные операторы, как, например, оператор принадлежности.
(6..9) === a
Результатом этой операции будет true, если значение переменной a входит в диапазон от 6 до 9 включительно.
Важно! Для того чтобы это сравнение работало правильно, диапазон должен идти первым аргументом.
Еще есть такая вещь, как оператор совпадения (=~). Он используется при проверке на наличие подстроки в тексте. Этот оператор имеет
обратную форму (!~).
Следует знать, что в Ruby де-юре нет понятия «логическая» или «булева» переменная, а есть два класса: TreueClass и FalseClass, в каждом по одному объекту – true и false.
Операции тестирования
Логическое значение не всегда бывает результатом операции сравнения. В Ruby довольно много методов имеет вот такую вопросительную форму:
a = text_stroke.empty?
Переменная a примет значение true, если текстовая переменная text_stroke пуста.
В Ruby также имеется специальный метод defined. Чтобы разобраться, как он работает, создадим скрипт:
puts defined? 1
a = 5
puts defined? a
puts defined? puts
puts defined? $_
Не выходе получим:
expression
local-variable
method
global-variable
То есть метод defined выдает текстовую строку, в которой описан тип тестируемого объекта.
Логические операции
При написании программы часто бывает так, что выполнение действия должно зависеть от нескольких условий. Или же необходимо как-то модифицировать результат сравнения. Для этого были придуманы логические операции.
С первой из них вы уже познакомились. Это логическое отрицание (NOT). В Ruby для этого существует оператор «!». Логическое значение обращается на противоположное.
a, b = 7, 5
l = !(a == b)
puts !l # на выходе - FALSE
Если требуется объединить результаты двух и более сравнений, нужно использовать оператор «&&» (логическое И(AND)). К примеру, результат этого выражения будет истинным лишь в случае совпадения всех трех условий:
a == 5 && b < 24 && c >=0
А если оператор «&&» заменить на «||» (логическое ИЛИ (OR)), то конструкция даст «true», если хотя бы одно из сравнений окажется верным. Как мы помним из школьного курса информатики, оператор И имеет приоритет перед оператором ИЛИ. То есть в скрипте
a, b, c, d = 7, 5, 2, 7
puts d==4 && c==2 || a==7 && b==5
вначале будут выполнено логическое И над парами результатов сравнений d==4, c==2 и a==7, b==5 соответственно. А уж потом над результатами работы оператора && будет выполнено логическое ИЛИ.
Операторы условного ветвления
Оператор if
Классический оператор ветвления ЕСЛИ… ТО. ЕСЛИ входное условие верно, ТО выполняется заданная последовательность команд.
if a == "" then puts "Входная строка пуста" end
Служебное слово-разделитель then можно заменить оператором «:».
if a == "" : puts «Входная строка пуста" end
Разделителем может послужить и перевод строки.
if a == ""
puts "Входная строка пуста" end
Вообще, если уж использовать переводы строки, то ветвление следует оформлять красиво.
if a == ""
puts "Входная строка пуста"
end
Если оформлять ветвление таким образом, в условную часть можно добавить и несколько команд.
if a == ""
puts "Входная строка пуста"
CountEmpty += 1
end
Теперь программа будет не только сообщать о пустых строках, но и считать их.
А если итогом ветвления служит одна-единственная команда, то оператор if можно использовать и «задним числом».
puts "Входная строка пуста" if a == ""
И даже вот так:
if a == ""
puts "Входная строка пуста"
end if (defined? a)
Проверка переменной а произойдет только в том случае, если она определена. Что предотвращает вылет программы с ошибкой.
Вариация else
Возможности оператора ЕСЛИ резко возрастают, если к нему добавить оператор ИНАЧЕ.
if in_str != "" then puts in_str else puts "* * *" end
В результате данного ветвления строка выводится на экран, если она не пустая. Вместо пустых строк будет выведено «* * *».
Вариация elsif
Нередко бывает, когда вариантов действий больше двух. Чтобы грамотно обрабатывать подобные конструкции, программисты придумали оператор ИЛИ ЖЕ (elsif). Он позволяет последовательно перебирать условия.
if in_str[0..4] == "Часть"
font_size = 3
elsif in_str[0..4] == "Глава"
font_size = 2
elsif in_str != ""
font_size = 1
else
font_size = nil
end
Данная программа определяет размер шрифта, использующийся при дальнейшей обработке текста, в зависимости от содержания входной строки. Для заголовков частей – побольше, глав – поменьше, обычного текста – еще меньше, а для пустых строк – никакой.
Вариативное присвоение. Оператор ternary
Для тех случаев, когда единственным результатом ветвления является присвоение значения переменной, в Ruby существует более компактная и элегантная конструкция.
font_size = if in_str[0..4] == "Часть"
3
elsif in_str[0..4] == "Глава"
2
elsif in_str != ""
1
else
0
end
Если же вариантов значения всего два, можно использовать еще более компактную конструкцию:
переменная = условие == ? значение 1 : значение 2
Если условие верно, то переменная примет значение 1, если нет – значение 2. Например, в выражении
string = string == "" ? "
" : "
текстовая строка будет заменена тэгом
либо отбита тэгами
А вот вариации elsif в ветвлении unless использовать нельзя. Потому как даже представить вереницу не выполняющихся условий крайне сложно. На первый взгляд, наличие «обратного» ветвления может показаться излишеством, чрезмерной данью эстетике. Но если принять во внимание, что в Ruby есть довольно много методов, дающих на выходе логическую переменную, все становится на свои места.
Селектор case
Вместо того чтобы набирать, как звенья цепочки, одну за другой вариации elsif, можно использовать более компактный и изящный инструмент. Речь идет об операторе case.
Для примера вновь обратимся к преобразованию текста в HTML.
index = ""
case in_str[0..4]
when "Часть": font_size = 3
index += in_str
when "Глава": font_size = 2
index += " " + in_str
when "": font_size = 0
else
font_size = 1
end
Чтобы показать, что в вариацию селектора может входить произвольное количество команд, мы заставили скрипт не только назначать размер шрифта, но и добавлять строки «Часть …» и «Глава …» в отдельную переменную-оглавление.
Если скрипт построен на присвоении значения, то его можно сделать более компактным.
index = ""
font_size = case in_str[0..4]
when "Часть": 3
index += in_str
when "Глава": index += " " + in_str
2
when "": 0
else 1
end
Обратите внимание, возможность вставлять в вариацию несколько команд сохранилась. Более того, в вариации «Глава» посторонняя команда поставлена перед значением для переменной font_size, однако это не помешало Ruby отработать скрипт правильно. Со «звеньями» elsif у вас этот номер не прошел бы.
На закуску предлагаю вам еще один пример. Вот так просто с помощью case можно организовать перекодировку символов. В данном случае – из кодовой таблицы windows-1251 в dos-866.
symb = case symb
when 0xC0..0xEF: symb -= 0x30
when 0xF0..0xFF: symb -= 0x10
when 0xA7: 0xF0 #Ё
when 0xB7: 0xF1 #ё
when 0xB8: 0xFC #№
else symb
end
Скрипт «гуртом» обрабатывает заглавную и строчную кириллицу, выделяя символы, которые выбиваются из общего ряда.
Юзич yuzich17@mail.ru
В программировании обыденны ситуации, когда нужно пропустить выполнение какого-либо фрагмента программы, перейдя сразу к следующему. Такие ситуации называются ветвлением.
К сожалению (или, вернее, к счастью!), в Ruby нет оператора безусловного перехода, наподобие goto МЕТКА. По причине отсутствия в этом языке всякого присутствия такого понятия, как «метка».
Все операторы ветвления подразумевают использование как минимум одного логического значения, которое может быть истинным (true) или ложным (false).
Логическое значение же, как правило, является результатом операции сравнения или тестирования. Давайте рассмотрим их.
Операции сравнения
Одно из наиболее частых сравнений – это проверка на равенство, или эквивалентность.
Для иллюстрации создадим скрипт.
a, b = 7, 5
l = a == b
puts l
Из этого текста можно уяснить следующее: во-первых, оператор «равенство» – это «==» (в отличие от «=» - оператора присваивания) и, во- вторых – результат операции сравнения можно назначить переменной.
Вообще, результатом операции сравнения является логическая, она же булева, величина, которая может принимать два разных значения – true (истинно) или false (ложно).
В частности, запустив этот скрипт, мы получим закономерное сообщение:
false
А заменив
a == b
на
a != b
получим, соответственно
true
То есть «!=» является оператором «не равно».
Продолжим эксперименты. Заменим в нашем скрипте две последние строки на
l = !(a == b)
puts !l
Запустив скрипт, получим результат:
false
Проведем разбор полетов. Оператор «!» есть логическое отрицание (обращение булева значения на противоположное). То есть результат сравнения, заключенного в скобки, был обращен (false превратилось в true) и присвоен переменной. А при выводе на экран - еще раз обращен. Из вышеприведенного примера следует, что если мы хотим обратить значение операции сравнения, оную операцию надобно заключать в скобки. Иначе транслятор истолкует оператор «!» как обращение первого значения.
Кстати, оператор обращения действует и на переменные с не логическим значением. А поскольку таковые значения приравнены к значению «true», результатом операции будет «false».
Кроме операторов «равно»-«не равно» в Ruby существует полный комплект операторов сравнения «больше»-«меньше»
a > b # a БОЛЬШЕ b
a < b # a МЕНЬШЕ b
a >= b # a БОЛЬШЕ либо РАВНО b
a <= b # a МЕНЬШЕ либо РАВНО b
А также вот такой универсальный оператор:
a <=> b
Результатом его работы будет не логическое значение, но целочисленное: -1 - если а меньше b; 0 – если a равно b; 1 – если a больше b. Так, со сравнением мы худо-бедно разобрались. Но в Ruby еще имеются такие интересные операторы, как, например, оператор принадлежности.
(6..9) === a
Результатом этой операции будет true, если значение переменной a входит в диапазон от 6 до 9 включительно.
Важно! Для того чтобы это сравнение работало правильно, диапазон должен идти первым аргументом.
Еще есть такая вещь, как оператор совпадения (=~). Он используется при проверке на наличие подстроки в тексте. Этот оператор имеет
обратную форму (!~).
Следует знать, что в Ruby де-юре нет понятия «логическая» или «булева» переменная, а есть два класса: TreueClass и FalseClass, в каждом по одному объекту – true и false.
Операции тестирования
Логическое значение не всегда бывает результатом операции сравнения. В Ruby довольно много методов имеет вот такую вопросительную форму:
a = text_stroke.empty?
Переменная a примет значение true, если текстовая переменная text_stroke пуста.
В Ruby также имеется специальный метод defined. Чтобы разобраться, как он работает, создадим скрипт:
puts defined? 1
a = 5
puts defined? a
puts defined? puts
puts defined? $_
Не выходе получим:
expression
local-variable
method
global-variable
То есть метод defined выдает текстовую строку, в которой описан тип тестируемого объекта.
Логические операции
При написании программы часто бывает так, что выполнение действия должно зависеть от нескольких условий. Или же необходимо как-то модифицировать результат сравнения. Для этого были придуманы логические операции.
С первой из них вы уже познакомились. Это логическое отрицание (NOT). В Ruby для этого существует оператор «!». Логическое значение обращается на противоположное.
a, b = 7, 5
l = !(a == b)
puts !l # на выходе - FALSE
Если требуется объединить результаты двух и более сравнений, нужно использовать оператор «&&» (логическое И(AND)). К примеру, результат этого выражения будет истинным лишь в случае совпадения всех трех условий:
a == 5 && b < 24 && c >=0
А если оператор «&&» заменить на «||» (логическое ИЛИ (OR)), то конструкция даст «true», если хотя бы одно из сравнений окажется верным. Как мы помним из школьного курса информатики, оператор И имеет приоритет перед оператором ИЛИ. То есть в скрипте
a, b, c, d = 7, 5, 2, 7
puts d==4 && c==2 || a==7 && b==5
вначале будут выполнено логическое И над парами результатов сравнений d==4, c==2 и a==7, b==5 соответственно. А уж потом над результатами работы оператора && будет выполнено логическое ИЛИ.
Операторы условного ветвления
Оператор if
Классический оператор ветвления ЕСЛИ… ТО. ЕСЛИ входное условие верно, ТО выполняется заданная последовательность команд.
if a == "" then puts "Входная строка пуста" end
Служебное слово-разделитель then можно заменить оператором «:».
if a == "" : puts «Входная строка пуста" end
Разделителем может послужить и перевод строки.
if a == ""
puts "Входная строка пуста" end
Вообще, если уж использовать переводы строки, то ветвление следует оформлять красиво.
if a == ""
puts "Входная строка пуста"
end
Если оформлять ветвление таким образом, в условную часть можно добавить и несколько команд.
if a == ""
puts "Входная строка пуста"
CountEmpty += 1
end
Теперь программа будет не только сообщать о пустых строках, но и считать их.
А если итогом ветвления служит одна-единственная команда, то оператор if можно использовать и «задним числом».
puts "Входная строка пуста" if a == ""
И даже вот так:
if a == ""
puts "Входная строка пуста"
end if (defined? a)
Проверка переменной а произойдет только в том случае, если она определена. Что предотвращает вылет программы с ошибкой.
Вариация else
Возможности оператора ЕСЛИ резко возрастают, если к нему добавить оператор ИНАЧЕ.
if in_str != "" then puts in_str else puts "* * *" end
В результате данного ветвления строка выводится на экран, если она не пустая. Вместо пустых строк будет выведено «* * *».
Вариация elsif
Нередко бывает, когда вариантов действий больше двух. Чтобы грамотно обрабатывать подобные конструкции, программисты придумали оператор ИЛИ ЖЕ (elsif). Он позволяет последовательно перебирать условия.
if in_str[0..4] == "Часть"
font_size = 3
elsif in_str[0..4] == "Глава"
font_size = 2
elsif in_str != ""
font_size = 1
else
font_size = nil
end
Данная программа определяет размер шрифта, использующийся при дальнейшей обработке текста, в зависимости от содержания входной строки. Для заголовков частей – побольше, глав – поменьше, обычного текста – еще меньше, а для пустых строк – никакой.
Вариативное присвоение. Оператор ternary
Для тех случаев, когда единственным результатом ветвления является присвоение значения переменной, в Ruby существует более компактная и элегантная конструкция.
font_size = if in_str[0..4] == "Часть"
3
elsif in_str[0..4] == "Глава"
2
elsif in_str != ""
1
else
0
end
Если же вариантов значения всего два, можно использовать еще более компактную конструкцию:
переменная = условие == ? значение 1 : значение 2
Если условие верно, то переменная примет значение 1, если нет – значение 2. Например, в выражении
string = string == "" ? "
" : "
"+string+"
"текстовая строка будет заменена тэгом
либо отбита тэгами
- если она не пустая.
Обратный вариант - unless
Для пущего удобства программирования в Ruby добавлен оператор-антипод ветвлению if – оператор unless. В нем основное действие выполняется, если условие НЕ ВЕРНО, то есть имеет значение FALSE.
unless in_str == "" : puts in_str else puts "* * *" end
Как и if, оператор unless допускает использование «постфактум»
a = "
" + a + "
" unless a == ""А вот вариации elsif в ветвлении unless использовать нельзя. Потому как даже представить вереницу не выполняющихся условий крайне сложно. На первый взгляд, наличие «обратного» ветвления может показаться излишеством, чрезмерной данью эстетике. Но если принять во внимание, что в Ruby есть довольно много методов, дающих на выходе логическую переменную, все становится на свои места.
Селектор case
Вместо того чтобы набирать, как звенья цепочки, одну за другой вариации elsif, можно использовать более компактный и изящный инструмент. Речь идет об операторе case.
Для примера вновь обратимся к преобразованию текста в HTML.
index = ""
case in_str[0..4]
when "Часть": font_size = 3
index += in_str
when "Глава": font_size = 2
index += " " + in_str
when "": font_size = 0
else
font_size = 1
end
Чтобы показать, что в вариацию селектора может входить произвольное количество команд, мы заставили скрипт не только назначать размер шрифта, но и добавлять строки «Часть …» и «Глава …» в отдельную переменную-оглавление.
Если скрипт построен на присвоении значения, то его можно сделать более компактным.
index = ""
font_size = case in_str[0..4]
when "Часть": 3
index += in_str
when "Глава": index += " " + in_str
2
when "": 0
else 1
end
Обратите внимание, возможность вставлять в вариацию несколько команд сохранилась. Более того, в вариации «Глава» посторонняя команда поставлена перед значением для переменной font_size, однако это не помешало Ruby отработать скрипт правильно. Со «звеньями» elsif у вас этот номер не прошел бы.
На закуску предлагаю вам еще один пример. Вот так просто с помощью case можно организовать перекодировку символов. В данном случае – из кодовой таблицы windows-1251 в dos-866.
symb = case symb
when 0xC0..0xEF: symb -= 0x30
when 0xF0..0xFF: symb -= 0x10
when 0xA7: 0xF0 #Ё
when 0xB7: 0xF1 #ё
when 0xB8: 0xFC #№
else symb
end
Скрипт «гуртом» обрабатывает заглавную и строчную кириллицу, выделяя символы, которые выбиваются из общего ряда.
Юзич yuzich17@mail.ru
Компьютерная газета. Статья была опубликована в номере 29 за 2010 год в рубрике программирование