Я столкнулся со следующим кодом Ruby:
class MyClass
attr_accessor :items
...
def each
@items.each{|item| yield item}
end
...
end
Что делает метод each
? В частности, я не понимаю, что делает yield
.
Я столкнулся со следующим кодом Ruby:
class MyClass
attr_accessor :items
...
def each
@items.each{|item| yield item}
end
...
end
Что делает метод each
? В частности, я не понимаю, что делает yield
.
Это пример, конкретизирующий ваш пример кода:
class MyClass
attr_accessor :items
def initialize(ary=[])
@items = ary
end
def each
@items.each do |item|
yield item
end
end
end
my_class = MyClass.new(%w[a b c d])
my_class.each do |y|
puts y
end
# >> a
# >> b
# >> c
# >> d
each
перебирает коллекцию. В этом случае он перебирает каждый элемент в массиве @items
, инициализированном/созданном, когда я выполнил оператор new(%w[a b c d])
.
yield item
в методе MyClass.each
передает item
блоку, прикрепленному к my_class.each
. Полученный item
назначается локальному y
.
Это помогает?
Теперь немного подробнее о том, как работает each
. Используя то же определение класса, вот некоторый код:
my_class = MyClass.new(%w[a b c d])
# This points to the `each` Enumerator/method of the @items array in your instance via
# the accessor you defined, not the method "each" you've defined.
my_class_iterator = my_class.items.each # => #<Enumerator: ["a", "b", "c", "d"]:each>
# get the next item on the array
my_class_iterator.next # => "a"
# get the next item on the array
my_class_iterator.next # => "b"
# get the next item on the array
my_class_iterator.next # => "c"
# get the next item on the array
my_class_iterator.next # => "d"
# get the next item on the array
my_class_iterator.next # =>
# ~> -:21:in `next': iteration reached an end (StopIteration)
# ~> from -:21:in `<main>'
Обратите внимание, что на последнем next
итератор выпал из конца массива. Это потенциальная ловушка для НЕ использования блока, потому что, если вы не знаете, сколько элементов в массиве, вы можете запросить слишком много элементов и получить исключение.
Использование each
с блоком будет перебирать приемник @items
и останавливаться, когда он достигает последнего элемента, избегая ошибки и сохраняя чистоту и чистоту.
return to_enum(:each) unless block_given?
в #each
, который вернет Enumerator
, если блок не задан, что позволяет такие вещи, как collection.each.take(10)
.
- person Kris; 18.05.2018
Когда вы пишете метод, который принимает блок, вы можете использовать ключевое слово yield
для выполнения блока.
Например, each
можно было бы реализовать в классе Array следующим образом:
class Array
def each
i = 0
while i < self.size
yield( self[i] )
i = i + 1
end
end
end
MyClass#each
берет блок. Он выполняет этот блок один раз для каждого элемента массива items
экземпляра, передавая текущий элемент в качестве аргумента.
Его можно использовать так:
instance = MyClass.new
instance.items = [1, 2, 3, 4, 5]
instance.each do |item|
puts item
end
Метод Ruby, который получает блок кода, вызывает его, вызывая его с помощью ключевого слова yield
. Его можно использовать для перебора списка, но это не итератор, как в других языках.
Вот хороший объяснение, которое объясняет это лучше, чем я когда-либо был бы в состоянии.
Насколько я понимаю, yield выполняет код из блока.
def name
puts "A yield will be called with id of 12"
yield 12
puts "A yield will be called with id of 14"
yield 14
end
name {|i| puts "I am called by yield name #{i}"}
Вывод:
Выход будет вызван с идентификатором 12
Меня зовут выходным именем 12
Выход будет вызван с идентификатором 14
Меня зовут выходным именем 14
Как работает доходность?
Поэтому, когда функция name
запускается везде, где приходит yield, запускается блочный код. Что name {|i| puts "I am called by yield name #{i}"}
Вы можете видеть, что слово yield 12
yield запускает код внутри блока, передавая 12 в качестве значения i
.
Вот пример игры для него:
def load_game
puts "Loading"
yield
end
load_game { puts "Game loaded" }
Это напечатает game loaded
сразу после печати loading
:
Загрузка
Игра загружена
yield
сообщает ruby, что нужно вызвать блок, переданный методу, передав ему свой аргумент.
yield
выдаст ошибку, если метод не был вызван с блоком, где оператор return
не выдает ошибку.
return
может отправлять только одиночные значения, тогда как Yield
возвращает объект огромных значений.
Чистый эффект заключается в том, что вызов .each для экземпляра MyClass аналогичен вызову .each для .items этого экземпляра.
Как новичок, просмотр ряда ответов сбил меня с толку, пока я не наткнулся на ответ Абхи.
команда yield приостанавливает выполнение кода в методе и вместо этого передает управление обратно блоку кода, который ее вызвал, выполняет этот код, а затем продолжает выполнение остальной части метода после этого. Вот пример, который прояснил это для меня:
def hello
puts "hello"
yield
puts "world"
end
hello do
puts "there"
end
Вывод:
Привет
там
Мир
Как сказал cpm, он берет блок и выполняет его.
Простой пример:
def my_method
yield
end
my_method do
puts "Testing yield"
end
Testing yield
=> nil