Быстрое знакомство с Ruby
Андрей Васильев
2020
Язык Ruby
Скриптовый (интепретируемый) язык
Приложение на языке Ruby всегда выполняется с помощью интерпретатора, как во время разработки, так и во время использования конечными пользователями
Объектно-ориентированный язык
Объект — это базовый элемент приложения, имеющий состояние и методы, использующие данное состояние
Структура объектов описывается с помощью классов
Для создания объектов (экземпляров классов) используется метод класса new
, конструктор
Динамически типизированный
Переменные могут содержать в себе ссылки на объекты любых типов
Запуск приложений на Ruby
Для выполнения приложения на языке Ruby потребуется:
Интерпретатор языка Ruby
Набор библиотек, gems, от которых зависит код приложения
Код самого приложения
Запуск приложения сводится к вызову интерпретатора Ruby с указанием файла, начинающего исполнение приложения
Актуальные интерпретаторы Ruby
Переменная PATH
Во всех операционных системах есть переменная окружения PATH, содержащая набор каталогов, в которых происходит поиск исполняемых файлов
Если каталога с исполняемым файлом нет в PATH, то для его запуска надо указать абсолютный путь
Руководство по настройке PATH
IRB — интерактивный интерпретатор Ruby
Вместе с обычным интерпретатором языка ruby
, предназначенным для выполнения приложений, поставляется также интерактивный интерпретатор irb
Данное приложение работает по следующему принципу:
Пользователь вводит строку на языке Ruby и нажимает Enter
Приложение считывает данную строку, разбирает и выполняет
Выполнение выражения изменяет состояние IRB
Результат выполнения выражения выводится пользователю
Приложение ожидает ввод от пользователя
IRB позволяет
Быстро проверять свои идеи
Узнавать детали работы методов
Пример из приложения
Приложение «Музыкальный автомат» должно обрабатывать данные о песнях. Опишем структуру объектов типа «песня» с помощью класса Song
:
Создадим две переменные first_song
и second_song
, запишем в них ссылки на создаваемые объекты
first_song = Song .new('Калинка малинка' )
second_song = Song .new('Смуглянка' )
Объекты в Ruby
У каждого объекта есть уникальный идентификатор, даже если нет данных
Объекты содержат переменные экземпляра, описывающие его состояние
Объекты содержат методы, которые могут изменять переменные экземпляра
Методы могут быть публичными, приватными и защищёнными
Публичные методы могут быть вызваны любыми объектами
Приватные методы могут быть вызваны только этим объектом
Защищённые методы могут быть вызваны в рамках дерева наследования
Вызов метода удобно моделировать как отправку сообщения между объектами
Модель вызова метода
Сообщение включает название метода и набор аргументов для него
Обработка вызова метода
При получении сообщения объект начинает поиск метода по имени
Когда метод найден, ему передаются все аргументы
Если метод не найден или количество аргументов не совпало, то будет выброшено исключение
Более детально логику поиска методов обсудим на следующих занятиях
Примеры вызова методов
"gin joint" .length
"Rick" .index("c" )
42 .even?
sam.play(song)
Часть кода до точки называется получателем
После точки вводится имя метода
Аргументы метода передаются в круглых скобках
Числа в Ruby являются объектами, они содержат методы
Базовые элементы языка
При написании кода на языке Ruby вам всегда будут доступны:
Метод puts
модуля Kernel
puts
выводит переданные аргументы на стандартный поток вывода
puts 'Hello, world!'
puts 42 .even?
puts 2 **10
Вывод в Ruby
puts
выводит аргументы на стандартный поток вывода и добавляет перенос на следующую строку
print
не добавляет перенос на новую строку
printf
позволяет выводить информацию согласно формату, как в Си
p
выводит информацию в формате, пригодном для отладки
pp
выводит информацию для отладки сложных объектов, пригодной для восприятия разработчиком
Простейший метод
def say_goodnight(name)
result = "Good night, " + name
return result
end
# Вызываем метод
puts say_goodnight("John-Boy" )
puts say_goodnight("Mary-Ellen" )
Выражения на разных линиях не надо разделять с помощью точки с запятой
Комментарии начинаются с символа #
При объявлении переменные должны быть инициализрованы
Определение метода
Определение начинается с ключевого слова def
Затем следует имя метода, слова в названии которого разделены подчёркиваниями
В круглых скобках перечисляются аргументы
Тело метода завершается ключевым словом end
Результат работы возвращается с помощью return
def say_goodnight(name)
result = "Good night, " + name
return result
end
Вызов метода
Передавайте методу столько обязательных аргументов, сколько он ожидает
При вызове метода можно не указывать круглые скобки:
puts say_goodnight("John-Boy" )
puts(say_goodnight("John-Boy" ))
Обычно скобки опускаются только в самых простых случаях:
Если метод не имеет аргументов, не указывайте скобки
Если метод имеет аргументы, указывайте скобки за исключением стандартных методов вывода информации (puts
, print
и p
)
Также следует использовать соглашения конкретных библиотек
Строки
Для определения строк обычно используются литералы
Литералы описываются с помощью одинарных и двойных кавычек
Строки в одинарных кавычках не обрабатываются
Обработка строк в двойных кавычках
Замена специальных символов (\n, \t)
Заполнение результатов выражений #{ ... }
В отличие от Java строки можно изменять
Вместо конкатенации строк используйте шаблонные строки
def say_goodnight(name)
result = "Good night, #{ name.capitalize} "
return result
end
Возвращаемое значение
Для возвращения значения из метода в произвольный момент используется ключевое слово return
Результат вычисления последнего выражения в методе является его значением
def say_goodnight(name)
"Good night, #{ name.capitalize} "
end
return
обычно используется когда результат становится известен заранее:
При возникновении ошибочного состояния
Когда действие не надо выполнять для данных аргументов
Когда аргументы не прошли проверку
Наименования элементов
Первый символ элемента приложения описывает его назначение
Имена локальных переменных, параметров методов и имена методов должны начинаться со строчной буквы или подчёркивания
Имена глобальных переменных начинаются со знака $
Имена переменных экземпляра, начинаются со знака @
Имена переменных класса, начинаются с @@
Имена классов, модулей и констант начинаются с заглавной буквы
Имена методов могут заканчиваться символами ?
, !
, =
Слова в именах переменных разделяются подчёркиваниями
Слова в названиях классов начинаются с заглавной буквы
Массивы и ассоциативные массивы
Массивы являются индексируемыми коллекциями
Обычные массивы индексируются числами
Ассоциативные массивы — любыми объектами
Коллекции всегда являются динамическими
В коллекции могут находится любые объекты
Массивы
a = [1 , 'cat' , 3.14 ]
puts "Первый элемент #{ a[0 ]} "
# Установим новое значение 3 элементу массива
a[2 ] = nil
puts "Массив сейчас #{ a.inspect} "
Создание массивов
Объект nil
Во многих языках программирования есть понятие «отсутствующего значения», например null
в Java
nil
— объект, обозначающий отсутствие значения
Массивы из строк
a = ['ant' , 'bee' , 'cat' , 'dog' , 'elk' ]
a[0 ] # => "ant"
a[3 ] # => "dog"
# Создание такого же массива из строки
a = %w{ ant bee cat dog elk }
a[0 ] # => "ant"
a[3 ] # => "dog"
Ассоциативные массивы
Для создания литералов ассоциативных массивов используются фигурные скобки
Элементами описания являются пары ключ-значение, разделённые «=>
»: ключ =>
значение
Ключи должны быть уникальными для массива
Пары разделяются друг от друга запятыми
inst_section = {
'cello' => 'string' ,
'clarinet' => 'woodwind' ,
'drum' => 'percussion'
}
print inst_section['cello' ]
Значение по умолчанию
Если обратиться к несуществующему элементу, тогда будет возвращено значение nil
Иногда желательно, чтобы массив возвращал осмысленное значение по умолчанию
# Создаём массив со значением по умолчанию
histogram = Hash .new(0 )
histogram['ruby' ] # => 0
histogram['ruby' ] = histogram['ruby' ] + 1
histogram['ruby' ] # => 1
Проблема уникальных значений
Часто при написании приложения нам необходимо описать уникальные значения и обращаться к ним с помощью слов
Для решения этих задач можно прибегнуть либо к константам, либо к строкам
Проблемами использования констант являются:
Мы не можем выполнить обратное преобразование надёжно
Пользователь может использовать значение вместо константы
Константы необходимо заранее объявить и подключить объявление во всех местах использования
Проблемой использования строк является то, что под каждую из них необходимо выделить отдельную память
Символы в Ruby
В Ruby есть специальный тип данных Symbol
, который позволяет описать уникальное именованное значение
В отличие от констант их не надо определять заранее и они всегда будут уникальны
В отличие от констант они не имеют связанного с ними значения
В отличие от строк они не занимают оперативную память при использовании
В отличие от строк они быстрее сравниваются друг с другом
В отличие от строк и констант их стоит рассматривать как код, а не как данные
Символы создаются с помощью синтаксиса :north
или :"north"
walk(:north )
def walk(direction)
if direction == :north
...
end
Символы как ключи хешей
Символы часто используются в качестве ключей хешей
inst_section = {
:cello => 'string' ,
:clarinet => 'woodwind'
}
Поддержка таких хешей добавлена в синтаксис языка
inst_section = {
cello: 'string' ,
clarinet: 'woodwind'
}
Управляющие конструкции
Ruby поддерживает знакомые вам конструкции
if
для описания условной логики
while
для описания циклических действий
today = Time .now
if today.saturday?
puts "Do chores around the house"
elsif today.sunday?
puts "Relax"
else
puts "Go to work"
end
Блок описания заканчивается ключевым словом end
Выражения в условиях
Любое выражение может являться условием. Ложными значениями являются только false
и nil
. Все остальные значения являются верными.
while weight < 100 && num_pallets <= 5
pallet = next_pallet()
weight += pallet.weight
num_pallets += 1
end
Метод gets
получает строку со стандартного потока ввода
При достижении конца потока gets
возвращает nil
while line = gets
puts line.downcase
end
Модификаторы выражений
Условные операторы могут выступать в роли модификатора выражения
if radiation > 3000
puts "Danger, Will Robinson"
end
puts "Danger, Will Robinson" if radiation > 3000
Аналогично для операторов цикла
square = 4
while square < 1000
square = square*square
end
square = square*square while square < 1000
Однако не стоит таким образом описывать сложные выражения
Нумерованные циклы
В Ruby также существует цикл for
, который позволяет выполнить действие для элемента коллекции
for value in [1 , 2 , 3 ] do
puts value
end
Однако для выполнения действий несколько раз удобнее воспользоваться методом times
у целых чисел:
3 .times do |value|
puts value
end
Регулярные выражения
Регулярное выражение — способ описать шаблон, которому должна соответствовать строка
Реализованы как часть языка, а не в виде библиотеки
Регулярные выражения — объекты
/Perl|Python/
— либо строка целиком Perl
, либо целиком Python
/ab+c/
— a
, за которым следует несколько b
, затем c
Выражения можно сравнивать со строками с помощью ~=
Метод sub
строки позволяет заменять подстроки
line = "Perl and Python are scripting languages"
newline = line.sub(/Perl/ , 'Ruby' )
newerline = newline.gsub(/Python/ , 'Ruby' )
Блоки и итераторы
Вы можете ассоциировать некоторый блок кода с методом
Блоки могут быть использованы для реализации
Обратных вызовов callbacks
Передачи управления из метода блоку
Блоки можно сравнить с анонимным методом
Примеры блоков
{ puts "Hello" } # Для одного выражения
do # Для нескольких выражений
club.enroll(person)
person.socialize
end
Ассоциация блока с методом
Для ассоциации блока с методом его необходимо описать сразу после вызова метода
greet { puts "Hi" }
verbose_greet("Dave" ) { puts "Hi" }
Вызов ассоциированного блока кода
Вызов осуществляется с помощью ключевого слова yeild
def call_block
puts "Start of the method"
yield
puts "End of the method"
end
call_block { puts "In the block" }
Аргументы блоков
Блокам могут быть переданы аргументы
Аргументы описываются между вертикальными линиями
def who_says_what
yield ("Dave" , "hello" )
yield ("Andy" , "goodbye" )
end
who_says_what do |person, phrase|
puts " #{ person} says #{ phrase} "
end
Итераторы
Итератором называется метод коллекций, принимающий блок
Под коллекцией понимаются как встроенные классы массива и хеша, так и классы, созданные пользователями
Коллекции поддерживают множество итераторов
animals = %w( ant bee cat dog )
animals.each { |animal| puts animal }
3 .upto(6 ) { |number| print number }
('a' ..'e' ).each { |character| print character }
Примеры полезных итераторов
data = [5 , 66 , 13 , 24 , 46 ]
# Все элементы с позицией
data.each_with_index do |number, index|
puts " #{ index} : #{ number} "
end
# Все ли элементы удовлетворяют условию: являются нечётными
data.all? { |number| number.odd? }
# Создать новый массив на основе текущего: квадраты чисел
data.map { |number| number**2 }
# Посчитать общую характеристику: сумму чисел
data.reduce { |memo, number| number + memo }
# Выбрать часть элементов массива
data.select { |number| number > 40 }
Обработка аргументов командной строки
Доступ к списку аргументов
Массив ARGV
содержит список строк, которые были переданы в виде аргументов командной строки
puts "You gave #{ ARGV .size} arguments"
p ARGV
Обработка файлов, переданных приложению
Если приложение ориентировано на обработку текстовых файлов, то пути к ним можно передать в качестве аргументов
В приложении к ним можно обратиться как к одному большому файлу через специальный объект ARGF