Блоки

За время прохождения курса мы научились писать методы не привязанные к объекту (функции), методы объекта, но что если нам нужно получать от пользователя какое-то действие дополнительно ? Например наш метод выполняет свою работу, но на каком-то этапе мы хотим дать пользователю возможность повлиять на работу программы ?

Блок - это анонимная функция, которая может быть передана в другую функцию в качестве аргумента, и выполнена внутри тела функции. Блоки могут быть записаны с помощью фигурных скобок {} или 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

  • Сгруппировать полученную выборку в хэш таблицу по названию отдела в котором они работают

Рекомендации:

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