Class: Generator

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/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
# File 'lib/generator.rb', line 69

def initialize(enum = nil, &block)
  if enum
    @block = proc { |g|
	enum.each { |x| g.yield x }
    }
  else
    @block = block
  end

  @index = 0
  @queue = []
  @cont_next = @cont_yield = @cont_endp = nil

  if @cont_next = callcc { |c| c }
    @block.call(self)

    @cont_endp.call(nil) if @cont_endp
  end

  self
end

Instance Method Details

#currentObject

Returns the element at the current position.


141
142
143
144
145
146
147
# File 'lib/generator.rb', line 141

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

  @queue.first
end

#eachObject

Rewinds the generator and enumerates the elements.


157
158
159
160
161
162
163
164
165
# File 'lib/generator.rb', line 157

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
# File 'lib/generator.rb', line 102

def end?()
  if @cont_endp = callcc { |c| c }
    @cont_yield.nil? && @queue.empty?
  else
    @queue.empty?
  end
end

#indexObject

Returns the current index (position) counting from zero.


116
117
118
# File 'lib/generator.rb', line 116

def index()
  @index
end

#nextObject

Returns the element at the current position and moves forward.


126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/generator.rb', line 126

def next()
  if end?
    raise EOFError, "no more elements available"
  end

  if @cont_next = callcc { |c| c }
    @cont_yield.call(nil) if @cont_yield
  end

  @index += 1

  @queue.shift
end

#next?Boolean

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

Returns:

  • (Boolean)

111
112
113
# File 'lib/generator.rb', line 111

def next?()
  !end?
end

#posObject

Returns the current index (position) counting from zero.


121
122
123
# File 'lib/generator.rb', line 121

def pos()
  @index
end

#rewindObject

Rewinds the generator.


150
151
152
153
154
# File 'lib/generator.rb', line 150

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

  self
end

#yield(value) ⇒ Object

Yields an element to the generator.


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

def yield(value)
  if @cont_yield = callcc { |c| c }
    @queue << value
    @cont_next.call(nil)
  end

  self
end