在Ruby中有类似Python生成器的东西吗?

bod*_*ydo 36 ruby lazy-evaluation

我是Ruby的新手,有没有办法yield从Ruby函数中获取值?如果有,怎么样?如果没有,我有什么选择来编写惰性代码?

Chu*_*uck 51

Ruby的yield关键字与具有相同名称的Python关键字有很大不同,所以不要被它混淆.Ruby的yield关键字是用于调用与方法关联的块的语法糖.

最接近的等价物是Ruby的Enumerator类.例如,相当于Python:

def eternal_sequence():
  i = 0
  while True:
    yield i
    i += 1
Run Code Online (Sandbox Code Playgroud)

这是:

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
Run Code Online (Sandbox Code Playgroud)

您还可以使用现有枚举方法创建枚举器enum_for.例如,('a'..'z').enum_for(:each_with_index)为您提供小写字母的枚举器及其在字母表中的位置.你可以使用each_with_index1.9中的标准Enumerable方法免费获得这个,所以你可以编写('a'..'z').each_with_index来获取枚举器.

  • `0.step`是自1.9以来的永恒枚举者 (3认同)

Mic*_*ohl 23

我已经看过以这种方式使用的Fibers,看看这篇文章中的一个例子:

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 }
Run Code Online (Sandbox Code Playgroud)

  • 事实上,在Ruby 1.9中,`Enumerator`是使用`Fiber`实现的.实际上,这是添加它们的主要原因之一,因为在Ruby 1.8中Enumerator的使用延续,但这是a)相当笨拙而且b)当时将继续从Ruby语言中删除. (6认同)

Mar*_*cny 12

如果你想懒洋洋地产生价值,@ Chuck的回答是正确的.

如果你想懒洋洋地迭代一个集合,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]
Run Code Online (Sandbox Code Playgroud)


Ers*_*nci 5

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
Run Code Online (Sandbox Code Playgroud)

https://ruby-doc.org/stdlib-1.8.7/libdoc/generator/rdoc/Generator.html