Что делает ключевое слово yield в Ruby?

Я столкнулся со следующим кодом Ruby:

class MyClass
    attr_accessor :items
    ...
    def each
        @items.each{|item| yield item}
    end
    ...
end

Что делает метод each? В частности, я не понимаю, что делает yield.


person Misha Moroshko    schedule 01.12.2010    source источник


Ответы (8)


Это пример, конкретизирующий ваш пример кода:

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 и останавливаться, когда он достигает последнего элемента, избегая ошибки и сохраняя чистоту и чистоту.

person the Tin Man    schedule 01.12.2010
comment
Вы имели в виду блок Начало-Конец, как указано здесь . Я новичок в рубине, поэтому пытаюсь выяснить, что вы имели в виду под блоком. - person Am33d; 10.04.2018
comment
Вы также увидите 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
person cpm    schedule 01.12.2010

Метод Ruby, который получает блок кода, вызывает его, вызывая его с помощью ключевого слова yield. Его можно использовать для перебора списка, но это не итератор, как в других языках.

Вот хороший объяснение, которое объясняет это лучше, чем я когда-либо был бы в состоянии.

person Gerhard    schedule 01.12.2010
comment
fwiw - я нашел эту страницу, чтобы дать более простое объяснение {code}yield{code } конкретно - person JoeyC; 31.10.2013

Насколько я понимаю, 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:

Загрузка

Игра загружена

person Ilyas karim    schedule 03.02.2017

yield сообщает ruby, что нужно вызвать блок, переданный методу, передав ему свой аргумент.

yield выдаст ошибку, если метод не был вызван с блоком, где оператор return не выдает ошибку.

return может отправлять только одиночные значения, тогда как Yield возвращает объект огромных значений.

person Kaleem Ullah    schedule 01.09.2015

Чистый эффект заключается в том, что вызов .each для экземпляра MyClass аналогичен вызову .each для .items этого экземпляра.

person Karl Knechtel    schedule 01.12.2010

Как новичок, просмотр ряда ответов сбил меня с толку, пока я не наткнулся на ответ Абхи.

команда yield приостанавливает выполнение кода в методе и вместо этого передает управление обратно блоку кода, который ее вызвал, выполняет этот код, а затем продолжает выполнение остальной части метода после этого. Вот пример, который прояснил это для меня:

def hello
    puts "hello"
    yield 
    puts "world"
end

hello do
    puts "there"
end 

Вывод:

Привет

там

Мир

person Ryanjr    schedule 11.09.2016

Как сказал cpm, он берет блок и выполняет его.

Простой пример:

def my_method
  yield
end


my_method do
  puts "Testing yield"
end

Testing yield
=> nil 
person Abhi    schedule 20.08.2014