web-dev-qa-db-ja.com

RubyにPythonジェネレーターのようなものはありますか?

私はRubyを初めて使用しますが、Ruby関数からyield値を取得する方法はありますか?はいの場合、どのようにですか?そうでない場合、遅延コードを作成するためのオプションは何ですか?

36
bodacydo

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)は、小文字の列挙子とアルファベットの位置を示します。これは、1.9のeach_with_indexのような標準のEnumerableメソッドを使用して無料で取得できるため、('a'..'z').each_with_indexと記述して列挙子を取得できます。

52
Chuck

私は 繊維 そのように使用されているのを見てきました。 この記事 からの例を見てください:

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 }
23
Michael Kohl

怠惰に値を生成することを探しているなら、@ 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]
12
Martin Konecny

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/generator/rdoc/Generator.html

5
Ersin Akinci