Да, поначалу это немного озадачивает.
В 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
(примечание: вы можете называть эту переменную как угодно, но имеет смысл называть ее 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?
Если не является необязательным, просто вызовите его.
ИЗМЕНИТЬ
@hmak создал repl.it для этих примеров: https://repl.it/@makstaks/blocksandyieldsrubyexample
person
OscarRyz
schedule
18.06.2010