Да, сначала это немного озадачивает.
В Ruby методы могут получить блок кода для выполнения произвольных сегментов кода.
Когда метод ожидает блок, он вызывает его, вызывая функцию yield
.
Это очень удобно, например, для перебора списка или для предоставления собственного алгоритма.
Возьмем следующий пример:
Я собираюсь определить класс Person
, инициализированный именем, и предоставить метод do_with_name
, который при вызове будет просто передавать name
атрибут, к полученному блоку.
class Person
def initialize( name )
@name = name
end
def do_with_name
yield( @name )
end
end
Это позволит нам вызвать этот метод и передать произвольный кодовый блок.
Например, напечатать имя, которое мы сделаем:
person = Person.new("Oscar")
#invoking the method passing a block
person.do_with_name do |name|
puts "Hey, his name is #{name}"
end
Выведет:
Hey, his name is Oscar
Обратите внимание, блок получает в качестве параметра переменную с именем name
(NB. Вы можете вызывать эту переменную как угодно, но этосмысл называть это name
).Когда код вызывает yield
, он заполняет этот параметр значением @name
.
yield( @name )
Мы могли бы предоставить другой блок для выполнения другого действия.Например, измените имя на обратное:
#variable to hold the name reversed
reversed_name = ""
#invoke the method passing a different block
person.do_with_name do |name|
reversed_name = name.reverse
end
puts reversed_name
=> "racsO"
Мы использовали точно такой же метод (do_with_name
) - это просто другой блок.
Этот пример тривиален.Более интересным является фильтрация всех элементов в массиве:
days = ["monday", "tuesday", "wednesday", "thursday", "friday"]
# select those which start with 't'
days.select do | item |
item.match /^t/
end
=> ["tuesday", "thursday"]
Или мы также можем предоставить собственный алгоритм сортировки, например, на основе размера строки:
days.sort do |x,y|
x.size <=> y.size
end
=> ["monday", "friday", "tuesday", "thursday", "wednesday"]
Я надеюсь, что это поможет вам лучше понять это.
Кстати, если блок является необязательным, вы должны назвать его следующим образом:
yield(value) if block_given?
Если не является обязательным, просто вызовите его.