Class: Generator

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/rubysl/generator/generator.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(enum = nil, &block) ⇒ Generator

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.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 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

Instance Method Details

#currentObject

Returns the element at the current position.



145
146
147
148
149
150
151
# File 'lib/rubysl/generator/generator.rb', line 145

def current
  if @queue.empty?
    raise EOFError, "no more elements available"
  end

  @queue.first
end

#eachObject

Rewinds the generator and enumerates the elements.



161
162
163
164
165
166
167
168
169
# File 'lib/rubysl/generator/generator.rb', line 161

def each
  rewind

  until end?
    yield self.next
  end

  self
end

#end?Boolean

Returns true if the generator has reached the end.

Returns:

  • (Boolean)


102
103
104
105
106
107
108
109
110
# 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

#indexObject

Returns the current index (position) counting from zero.



118
119
120
# File 'lib/rubysl/generator/generator.rb', line 118

def index
  @index
end

#nextObject

Returns the element at the current position and moves forward.

Raises:

  • (EOFError)


128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# 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?Boolean

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

Returns:

  • (Boolean)


113
114
115
# File 'lib/rubysl/generator/generator.rb', line 113

def next?
  !end?
end

#posObject

Returns the current index (position) counting from zero.



123
124
125
# File 'lib/rubysl/generator/generator.rb', line 123

def pos
  @index
end

#rewindObject

Rewinds the generator.



154
155
156
157
158
# File 'lib/rubysl/generator/generator.rb', line 154

def rewind
  initialize(nil, &@block) if @index.nonzero?

  self
end

#yield(value) ⇒ Object

Yields an element to the generator.



93
94
95
96
97
98
99
# File 'lib/rubysl/generator/generator.rb', line 93

def yield(value)
  # Running inside @fiber
  @queue << value
  Rubinius::Fiber.yield

  self
end