G
Greg Fitzgerald
Hey all,
I was playing with the Generator class the other day and decided I
didn't like that you can't break out while iterating through the
generator without first having already taken the time to iterate to
the element after the one you were at when you decided to break out.
The following code shows what I mean. It runs through
test_generator() twice, the first with the current implementation of
Generator, and the second with a new one I whipped up. It shows that
the current implementation takes longer to execute when breaking out
early. Also, outside of performance, I don't like that if I were
reading a stream of data and decided to break out, it would read one
too many lines--kind of an ugly side-effect.
Unfortunately, there's a significant bug in my version that I been
trying to crack all night and I can't figure it out for the life of
me. I want to be able to start iterating, then midway through, call
rewind(), and then start iterating through again. When I do that, it
leaves the stack in an odd place, and when I call each(), it will make
it all the way to the end then get caught in an infinite loop inside
next?().
So I guess my question is, if you create a continuation with callcc()
and then decide never to call that continuation, is there some way to
unwind the stack?
Thanks,
Greg
require 'generator'
def test_generator()
timeStart = Time.now
puts "start constructing at #{Time.now - timeStart}"
gen = Generator.new do |g|
for num in [1,2,3,4,5]
sleep(3)
g.yield(num)
end
end
puts "finished constructing at #{Time.now - timeStart}"
puts "getting first element at #{Time.now - timeStart}"
for num in gen
puts num
break if num == 3
puts "getting next element at #{Time.now - timeStart}"
end
puts "done at #{Time.now - timeStart}"
puts "\n\n"
end
puts "Ruby's Generator"
test_generator()
class EndOfGenerator < Exception
end
class Generator
def initialize(&closure)
@closure = closure
return rewind()
end
def each()
rewind()
while self.next?()
yield self.next()
end
return self
end
def rewind()
@closureEntryPoint = nil
@currentElement = nil
@nextElement = nil
@position = -1
#self.next?()
return self
end
def pos()
return @position
end
def current()
if @currentElement == nil
begin
self.next()
@nextElement = @currentElement
rescue EndOfGenerator
end
end
return @currentElement
end
def next?()
return true if @nextElement
begin
@nextElement = self.next()
return true
rescue EndOfGenerator
return false
end
end
def end?()
return !self.next?()
end
def next()
if @nextElement
@currentElement = @nextElement
else
@currentElement = callcc do |cc|
@generatorEntryPoint = cc
if @closureEntryPoint == nil
@closure.call(self)
raise EndOfGenerator.new()
else
@closureEntryPoint.call()
end
end
@position += 1
end
@nextElement = nil
return @currentElement
end
def yield(obj)
return callcc do |cc|
@closureEntryPoint = cc
@generatorEntryPoint.call(obj)
end
end
end
puts "Corrected generator"
test_generator()
I was playing with the Generator class the other day and decided I
didn't like that you can't break out while iterating through the
generator without first having already taken the time to iterate to
the element after the one you were at when you decided to break out.
The following code shows what I mean. It runs through
test_generator() twice, the first with the current implementation of
Generator, and the second with a new one I whipped up. It shows that
the current implementation takes longer to execute when breaking out
early. Also, outside of performance, I don't like that if I were
reading a stream of data and decided to break out, it would read one
too many lines--kind of an ugly side-effect.
Unfortunately, there's a significant bug in my version that I been
trying to crack all night and I can't figure it out for the life of
me. I want to be able to start iterating, then midway through, call
rewind(), and then start iterating through again. When I do that, it
leaves the stack in an odd place, and when I call each(), it will make
it all the way to the end then get caught in an infinite loop inside
next?().
So I guess my question is, if you create a continuation with callcc()
and then decide never to call that continuation, is there some way to
unwind the stack?
Thanks,
Greg
require 'generator'
def test_generator()
timeStart = Time.now
puts "start constructing at #{Time.now - timeStart}"
gen = Generator.new do |g|
for num in [1,2,3,4,5]
sleep(3)
g.yield(num)
end
end
puts "finished constructing at #{Time.now - timeStart}"
puts "getting first element at #{Time.now - timeStart}"
for num in gen
puts num
break if num == 3
puts "getting next element at #{Time.now - timeStart}"
end
puts "done at #{Time.now - timeStart}"
puts "\n\n"
end
puts "Ruby's Generator"
test_generator()
class EndOfGenerator < Exception
end
class Generator
def initialize(&closure)
@closure = closure
return rewind()
end
def each()
rewind()
while self.next?()
yield self.next()
end
return self
end
def rewind()
@closureEntryPoint = nil
@currentElement = nil
@nextElement = nil
@position = -1
#self.next?()
return self
end
def pos()
return @position
end
def current()
if @currentElement == nil
begin
self.next()
@nextElement = @currentElement
rescue EndOfGenerator
end
end
return @currentElement
end
def next?()
return true if @nextElement
begin
@nextElement = self.next()
return true
rescue EndOfGenerator
return false
end
end
def end?()
return !self.next?()
end
def next()
if @nextElement
@currentElement = @nextElement
else
@currentElement = callcc do |cc|
@generatorEntryPoint = cc
if @closureEntryPoint == nil
@closure.call(self)
raise EndOfGenerator.new()
else
@closureEntryPoint.call()
end
end
@position += 1
end
@nextElement = nil
return @currentElement
end
def yield(obj)
return callcc do |cc|
@closureEntryPoint = cc
@generatorEntryPoint.call(obj)
end
end
end
puts "Corrected generator"
test_generator()