Ruby equivalent of C#'s 'yield' keyword, or, creating sequences without preallocating memory

后端 未结 4 2029
遥遥无期
遥遥无期 2021-01-12 05:20

In C#, you could do something like this:

public IEnumerable GetItems()
{
    for (int i=0; i<10000000; i++) {
        yield return i;
           


        
相关标签:
4条回答
  • 2021-01-12 05:33

    Your specific example is equivalent to 10000000.times, but let's assume for a moment that the times method didn't exist and you wanted to implement it yourself, it'd look like this:

    class Integer
      def my_times
        return enum_for(:my_times) unless block_given?
        i=0
        while i<self
          yield i
          i += 1
        end
      end
    end
    
    10000.my_times # Returns an Enumerable which will let
                   # you iterate of the numbers from 0 to 10000 (exclusive)
    

    Edit: To clarify my answer a bit:

    In the above example my_times can be (and is) used without a block and it will return an Enumerable object, which will let you iterate over the numbers from 0 to n. So it is exactly equivalent to your example in C#.

    This works using the enum_for method. The enum_for method takes as its argument the name of a method, which will yield some items. It then returns an instance of class Enumerator (which includes the module Enumerable), which when iterated over will execute the given method and give you the items which were yielded by the method. Note that if you only iterate over the first x items of the enumerable, the method will only execute until x items have been yielded (i.e. only as much as necessary of the method will be executed) and if you iterate over the enumerable twice, the method will be executed twice.

    In 1.8.7+ it has become to define methods, which yield items, so that when called without a block, they will return an Enumerator which will let the user iterate over those items lazily. This is done by adding the line return enum_for(:name_of_this_method) unless block_given? to the beginning of the method like I did in my example.

    0 讨论(0)
  • 2021-01-12 05:47

    It's supported by Enumerator since Ruby 1.9 (and back-ported to 1.8.7). See Generator: Ruby.

    Cliche example:

    fib = Enumerator.new do |y|
      y.yield i = 0
      y.yield j = 1
      while true
        k = i + j
        y.yield k
        i = j
        j = k
      end
    end
    
    100.times { puts fib.next() }
    
    0 讨论(0)
  • 2021-01-12 05:48

    Without having much ruby experience, what C# does in yield return is usually known as lazy evaluation or lazy execution: providing answers only as they are needed. It's not about allocating memory, it's about deferring computation until actually needed, expressed in a way similar to simple linear execution (rather than the underlying iterator-with-state-saving).

    A quick google turned up a ruby library in beta. See if it's what you want.

    0 讨论(0)
  • 2021-01-12 05:56

    C# ripped the 'yield' keyword right out of Ruby- see Implementing Iterators here for more.

    As for your actual problem, you have presumably an array of arrays and you want to create a one-way iteration over the complete length of the list? Perhaps worth looking at array.flatten as a starting point - if the performance is alright then you probably don't need to go too much further.

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