Блоки
За время прохождения курса мы научились писать методы не привязанные к объекту (функции), методы объекта, но что если нам нужно получать от пользователя какое-то действие дополнительно ? Например наш метод выполняет свою работу, но на каком-то этапе мы хотим дать пользователю возможность повлиять на работу программы ?
Блок - это анонимная функция, которая может быть передана в другую функцию в качестве аргумента, и выполнена внутри тела функции. Блоки могут быть записаны с помощью фигурных скобок {}
или do..end
. Вот несколько примеров встроенных методов у массива, которые принимают блоки:
# блоки с фигурными скобками {}
# x в данном случае это переменная элемента текущего цикла
[1,2,3].each { |x| puts x }
# блоки с do..end
[1,2,3].each do |x|
puts x
end
Эти два примера идентичны и выполняют то же самое действие: выводят на экран все элементы массива [1,2,3]
.
Как использовать блоки в Ruby?
Для работы с блоками Ruby предоставляет ключевое слово yield, которое вызывает переданный блок. Вот пример:
def greet
# сначала мы напечатаем в консоль Hi
puts "Hi"
# потом выполним блок кода, который нам передал пользователь
yield
# После этого напечатается в консоль Bye
puts "Bye"
end
# Hi
# Welcome!
# Bye
greet { puts "Welcome!" }
Нужно понимать, что блок кода работает точно так же как и обычные методы. Последняя строчка выполнения блока возвращает результат, который может быть использован внутри метода, принимающего этот блок кода
def increase
# ожидаем, что блок вернет число
value = yield
# которое мы благополучно увеличим на 1
value + 1
end
puts increase { 10 } # = 11
Как передать параметры в блок?
Вы можете передавать параметры в блок, используя конструкцию | |
при объявлении блока:
# принимаем в виде аргумента имя, не путать с параметром блока
def say_hello(name)
# Печатаем в консоль приветствие с именем из аргумента
puts "Hi #{name}"
# передаем это имя в блок кода, чтобы пользователь мог с ним что то сделать
yield(name)
# печатаем в консоль прощание с именем из аргумента
puts "Bye #{name}"
end
# вызываем методов, передавая имя в аругменты метода
# Hi Alice
# Nice to meet you, Alice
# Bye Alice
say_hello("Alice") do |name|
# Так как в yield передается имя, то мы можем его использовать в блоке c помощью параметра между | |
puts "Nice to meet you, #{name}"
end
Представляйте каждый раз, что блок кода это возможность создать метод внутр идругого метода, тогда понимать как это работает будет проще.
Изучите наиболее часто встречющиеся методы у встроенных классов, которые принимают блок кода (примеры использования можете посмотреть в официальной документации):
Массивы (
Array
):each
- выполняет блок кода для каждого элемента массива.map
- создает новый массив, когда блок кода применяется к каждому элементу исходного массива.select
- возвращает только элементы, для которых выполнен условный блок кода.reject
возвращает элементы, для которых условный блок кода не выполнялся.group_by
- группирует элементы массива в хэш, используя результат выполнения блока кода в качестве ключа.inject
- сворачивает элементы массива, используя блок кода и начальное значение.
Хэши (
Hash
):each
- выполняет блок кода для каждой пары «ключ-значение» в хэше.map
- создает новый хэш, когда блок кода применяется к каждой паре «ключ-значение» исходного хэша.select
- возвращает хэш, содержащий только пары «ключ-значение», для которых выполнен условный блок кода.reject
- возвращает хэш, содержащий только пары «ключ-значение», для которых условный блок кода не выполнялся.group_by
- группирует пары «ключ-значение» в хэш, используя результат выполнения блока кода в качестве ключа.
Числа (
Numeric
):times
- выполняет блок кода указанное число раз.upto
- выполняет блок кода для каждого целого числа, начиная с текущего и заканчивая указанным.downto
- выполняет блок кода для каждого целого числа, начиная с текущего и заканчивая указанным в обратном порядке.
Строки (
String
):each_line
- выполняет блок кода для каждой строки, разделенной символом новой строки.gsub
- заменяет вхождения подстрок (Да, этот метод может еще и принимать блок кода)
Задание employees
Создайте классы компании Company
и сотрудника Employee
Класс компании состоит из названия и списка сотрудников.
class Company
attr_reader :title, :workers
def initialize(title, workers)
@title = title
@workers = workers
end
end
Класс сотрудника состоит из ег оимени и отдела в котором он работает
class Employee
attr_reader :name, :department
def initialize(name, department)
@department = department
@name = name
end
end
Кроме этого у вас есть список компаний с их сотруниками
def generate_companies
def generate_employee
names = ["John", "Emma", "Michael", "Jackson"]
surnames = ["Smith", "Johnson", "Williams"]
departments = ["Sales", "Marketing", "Finance"]
employee_full_name = "#{names.sample} #{surnames.sample}"
Employee.new(employee_full_name, departments.sample)
end
companies = []
company_names = ["Apple", "Amazon", "Google", "Facebook"]
for company_name in company_names
employees = []
15.times { employees << generate_employee }
companies << Company.new(company_name, employees)
end
companies
end
companies = generate_companies
Вам необходимо:
Получить всех работников их этих компаний в виде отдельного массива
Отфильтровать работников, чье имя начинается с J
Сгруппировать полученную выборку в хэш таблицу по названию отдела в котором они работают
Рекомендации:
Задание подразумевает выполнение с помощью методов-блоков. Для поиска нужного метода воспользуйтесь официальной документацией по Enumerable
zip_action
Напишите функцию zip(arr1, arr2)
, которая принимает в аргументах строковые массивы, а также блок кода, который выполняется для всех значений массива, длина строк которых одинаковая. Пример использования:
arr1 = ["Box", "Taxi", "Dictionary"]
arr2 = ["Test", "Frost", "Mark"]
# first и second должны иметь одинаковую длину и относиться к разным массивам
zip(arr1, arr2) do |first, second|
puts "#{first} and #{second} have same length"
end
# Taxi and Test have same length
# Taxi and Mark have same length
Last updated