Есть ли что-то вроде генераторов Python в Ruby? - PullRequest
34 голосов
/ 24 марта 2010

Я новичок в Ruby, есть ли способ yield значений из функций Ruby? Если да, то как? Если нет, то какие варианты написать ленивый код?

Ответы [ 4 ]

49 голосов
/ 24 марта 2010
Ключевое слово

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

Ближайшим эквивалентом является класс перечислителя Ruby.Например, эквивалент Python:

def eternal_sequence():
  i = 0
  while True:
    yield i
    i += 1

таков:

def eternal_sequence
  Enumerator.new do |enum|
    i = 0
    while true
      enum.yield i # <- Notice that this is the yield method of the enumerator, not the yield keyword
      i +=1
    end
  end
end

Вы также можете создать перечислители для существующих методов перечисления с помощью enum_for.Например, ('a'..'z').enum_for(:each_with_index) дает вам перечислитель строчных букв вместе с их местом в алфавите.Вы получаете это бесплатно с помощью стандартных методов Enumerable, таких как each_with_index в 1.9, так что вы можете просто написать ('a'..'z').each_with_index, чтобы получить перечислитель.

23 голосов
/ 24 марта 2010

Я видел Волокна , используемые таким образом, посмотрите на пример из этой статьи :

fib = Fiber.new do  
  x, y = 0, 1 
  loop do  
    Fiber.yield y 
    x,y = y,x+y 
  end 
end 
20.times { puts fib.resume }
12 голосов
/ 16 мая 2015

Если вы хотите лениво генерировать значения, ответ @ Чака правильный.

Если вы хотите лениво перебирать коллекцию, Ruby 2.0 представил новый перечислитель .lazy.

range = 1..Float::INFINITY
puts range.map { |x| x+1 }.first(10) #  infinite loop
puts range.lazy.map { |x| x+1 }.first(10) #  [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
5 голосов
/ 27 июня 2018

Ruby поддерживает генераторы из коробки, используя Enumerable::Generator:

require 'generator'

# Generator from an Enumerable object
g = Generator.new(['A', 'B', 'C', 'Z'])

while g.next?
  puts g.next
end

# Generator from a block
g = Generator.new { |g|
  for i in 'A'..'C'
    g.yield i
  end

  g.yield 'Z'
}

# The same result as above
while g.next?
  puts g.next
end

https://ruby -doc.org / STDLIB-1.8.7 / libdoc / генератор / RDoc / Generator.html

...