Если вы уже некоторое время программируете на Ruby, то, скорее всего, уже использовали встроенные методы, такие как #each
, #select
и #map
. Несмотря на то, что все говорят о магии Ruby, нет ничего особенного в том, как работают эти методы.
Эти методы обычно вызываются на коллекциях, таких как массивы и хэши, либо с помощью инлайн-блока (код, заключенный в фигурные скобки), либо многострочного блока (код, заключенный между do
и end
).
Как работают блоки?
Мы часто связываем блоки кода с методами, которые доступны нам через модуль Enumerable
. Пожалуйста, посмотрите на пример ниже:
- Инлайн-блок:
[1, 2, 3, 4, 5].select { |number| number.even? }
> [2, 4]
- Многострочный блок:
[1, 2, 3, 4, 5].select do |number|
number.odd?
end
> [1, 3, 5]
Для того чтобы полностью понять, как работают блоки, необходимо научиться создавать собственные пользовательские блоки, чтобы понять все тонкости!
Передача управления блоку
Любой метод в Ruby может быть связан с блоком, но то, что определяет, будет ли этот блок вызван или нет во время выполнения метода, — это специальное ключевое слово yield
.
roll_die { |number| puts "You rolled #{number}" }
Как мы могли бы реализовать этот метод?
def roll_die
puts "Method is executing..."
random_number = rand(1..6)
yield(random_number)
puts "Method is done!"
end
Поэтому, как только мы вызовем метод roll_die
с ассоциированным блоком, он выведет в консоль следующие строки:
> "Method is executing..."
> "You rolled 3"
> "Method is done!"
Что делает yield
, так это «приостанавливает» выполнение метода roll_die
и передает управление блоку, который с ним связан. Кроме того, он также передает параметр random_number
в качестве параметра блока!
Любопытным свойством блоков является то, что после завершения их выполнения они возвращают последнюю вычисленную строку кода в качестве конечного результата! Подождите секунду… что это значит?
Это значит, что вместо этого мы могли бы сделать что-то вроде этого:
- Перестановка кода в методе
roll_die
:
def roll_die
puts "Method is executing..."
random_number = rand(1..6)
result = yield(random_number)
puts "You rolled #{result}"
puts "Method is done!"
end
- Переупорядочивание блока кода, связанного с методом
roll_die
:
roll_die do |number|
puts "Rolling the die..."
number
end
В новом расположении случайное число возвращается методом yield
и присваивается переменной result
в методе roll_die
!
> "Method is executing..."
> "Rolling the die..."
> "You rolled 1"
> "Method is done!"
Предостережения о блоках Ruby
Если вы дочитали до этого места, вам, вероятно, интересно, что произойдет, если мы попытаемся выполнить yield
, но не будет связанного блока, которому можно передать управление. Для таких случаев мы можем использовать метод block_given?
. Посмотрите пример ниже:
- Определение метода
greeting(name)
:
def greeting(name)
if block_given?
yield(name)
else
"No block was given."
end
end
- Ассоциирование метода
greeting(name)
с блоком:
greeting(name) { |name| "Hello, #{name}!" }
Теперь есть 2 различных способа вызвать метод greeting(name)
:
# First way
greeting("Mary") { |name| "Hello, #{name}!" }
> "Hello, #{name}!"
# Second way
greeting("John")
> "No block was given."
Еще один любопытный вопрос связан с количеством параметров, передаваемых в yield
, которые в свою очередь отправляются как параметры блока в ваш блок. Принуждает/требует ли их Ruby или нет? Попробуйте, используя irb
— вы будете удивлены результатом!
Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы и/или предложения в комментариях ниже!