Most of the Enumerable methods can be used for fiber based generators
if Fiber is made enumerable.
class Fiber
include Enumerable
def each
loop { yield self.resume }
end
end
def fib_gen
Fiber.new {
a, b = 0, 1
while true
Fiber.yield a
a, b = b, a + b
end
}
end
Find the fibonacci number greater than 1000
fib_gen.find {|x| x > 1000 }
take first 10 fibonacci numbers
fib_gen.take 10
take_while numbers are smaller than 1000
fib_gen.take_while {|x| x < 1000 }
If it could be the default behavior, it would be very useful. Some
methods
won’t be applicable, particularly for non-terminating generators in
their
default incarnation viz. drop_while. It can either be re-written to
advance
the generator using Fiber#resume or it can be left upto the user to
handle
infinite sequences correctly.
If I understand correctly, writing generators was one of the purposes of
fibers. Why can’t fibers be made enumerable by default?
How would your example look if there was another generation that you
wanted to do concurrently? Because for the single threaded generator
case there is already a tool: Enumerator.new. The example in the
documentation even uses Fibonacci Numbers as example.
On Wed, 18 May 2011 20:12:43 +0900, Robert K. wrote:
y << a
take_while numbers are smaller than 1000
fib_gen.take_while {|x| x < 1000 }
For this Fiber would be the wrong tool.
The Enumerator.new uses Fibers internally. Check enumerator.c in the
sources;
besides that, how would you achieve the needed effect without
coroutines?
The Enumerator.new uses Fibers internally. Check enumerator.c in the
sources;
That’s an implementation detail of Enumerator. The point is that the
primary purpose of Fiber is to be able to build concurrency without
preemption but with manual control over when one task yields to
another task. Enumerator.new on the other hand is a tool for
generation of sequences of items which is precisely what the Fibonacci
example is all about. There is no concurrency.
besides that, how would you achieve the needed effect without coroutines?
class X
include Enumerable
Callback = Struct.new :code do
def <<(x)
code[x]
self
end
end
def initialize(&code) @code = code
end
def each(&b)
cb = Callback.new b @code[cb]
self
end
end
fib_gen = X.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}
Right. But aren’t semi co-routines(don’t send data; just receive) used to
implement generators?
Ruby fibers can be either semi-coroutines or full co-routines. And
while replacing continuations as the basis for implementing generators
was certainly an important motivation for implementing fibers in Ruby,
that’s not the only thing fibers can do, and enumerability may not
make sense for other fibers. So, usually, Enumerator::Generator is
used for generators (though that uses Fiber behind the scenes) and
Fiber is used directly for other use cases.