class Generator

Generator converts an internal iterator (i.e. an Enumerable object) to an external iterator.

Note that it is not very fast since it is implemented using continuations, which are currently slow.

Example

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

Public Class Methods

new(enum=nil, &block) click to toggle source

Creates a new generator either from an Enumerable object or from a block.

In the former, block is ignored even if given.

In the latter, the given block is called with the generator itself, and expected to call the yield method for each element.

# File lib/rubysl/generator/generator.rb, line 69
def initialize(enum=nil, &block)
  if enum
    @block = proc do |g|
      enum.each { |x| g.yield x }
    end
  else
    @block = block
  end

  @index = 0
  @queue = []

  @done = false

  @fiber = Rubinius::Fiber.new do
    @block.call(self)
    @done = true
    @fiber = nil
  end

  self
end

Public Instance Methods

current() click to toggle source

Returns the element at the current position.

# File lib/rubysl/generator/generator.rb, line 145
def current
  if @queue.empty?
    raise EOFError, "no more elements available"
  end

  @queue.first
end
each() { |next| ... } click to toggle source

Rewinds the generator and enumerates the elements.

# File lib/rubysl/generator/generator.rb, line 161
def each
  rewind

  until end?
    yield self.next
  end

  self
end
end?() click to toggle source

Returns true if the generator has reached the end.

# File lib/rubysl/generator/generator.rb, line 102
def end?
  return true if @done
  return false unless @queue.empty?

  # Turn the loop over and see if we hit the end
  @fiber.resume

  return @done
end
index() click to toggle source

Returns the current index (position) counting from zero.

# File lib/rubysl/generator/generator.rb, line 118
def index
  @index
end
next() click to toggle source

Returns the element at the current position and moves forward.

# File lib/rubysl/generator/generator.rb, line 128
def next
  raise EOFError, "end of iteration hit" if end?

  @fiber.resume if @queue.empty?

  # We've driven the fiber ahead and least once now, so we should
  # have values if there are values to be had

  unless @queue.empty?
    @index += 1
    return @queue.shift
  end

  raise EOFError, "end of iteration hit"
end
next?() click to toggle source

Returns true if the generator has not reached the end yet.

# File lib/rubysl/generator/generator.rb, line 113
def next?
  !end?
end
pos() click to toggle source

Returns the current index (position) counting from zero.

# File lib/rubysl/generator/generator.rb, line 123
def pos
  @index
end
rewind() click to toggle source

Rewinds the generator.

# File lib/rubysl/generator/generator.rb, line 154
def rewind
  initialize(nil, &@block) if @index.nonzero?

  self
end
yield(value) click to toggle source

Yields an element to the generator.

# File lib/rubysl/generator/generator.rb, line 93
def yield(value)
  # Running inside @fiber
  @queue << value
  Rubinius::Fiber.yield

  self
end