Are there something like Python generators in Ruby?

后端 未结 4 901
遇见更好的自我
遇见更好的自我 2020-12-13 09:09

I am new to Ruby, is there a way to yield values from Ruby functions? If yes, how? If not, what are my options to write lazy code?

相关标签:
4条回答
  • 2020-12-13 09:53

    I've seen Fibers used in that way, look at an example from this article:

    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 }
    
    0 讨论(0)
  • 2020-12-13 09:56

    Ruby supports generators out of the box using 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

    0 讨论(0)
  • 2020-12-13 10:09

    If you are looking to lazily generate values, @Chuck's answer is the correct one.

    If you are looking to lazily iterate over a collection, Ruby 2.0 introduced the new .lazy enumerator.

    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]
    
    0 讨论(0)
  • 2020-12-13 10:16

    Ruby's yield keyword is something very different from the Python keyword with the same name, so don't be confused by it. Ruby's yield keyword is syntactic sugar for calling a block associated with a method.

    The closest equivalent is Ruby's Enumerator class. For example, the equivalent of the Python:

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

    is this:

    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
    

    You can also create Enumerators for existing enumeration methods with enum_for. For example, ('a'..'z').enum_for(:each_with_index) gives you an enumerator of the lowercase letters along with their place in the alphabet. You get this for free with the standard Enumerable methods like each_with_index in 1.9, so you can just write ('a'..'z').each_with_index to get the enumerator.

    0 讨论(0)
提交回复
热议问题