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


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

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

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

8 32

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

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

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

return может отправлять только единичные значения, где as 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

Как работает yield?

Поэтому, когда функция name выполняется везде, где появляется выход, выполняется блочный код. Это 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:

Загрузка

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

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

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

Команда 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