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?
I am not sure how that interacts with the original intent of Fibers:
their main purpose is to bring coroutines to Ruby.
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.
fib_gen = Enumerator.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}
Now you can do exactly the same as you did with your example
# 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 }
For this Fiber would be the wrong tool.
Kind regards
robert
···
On Wed, May 18, 2011 at 9:39 AM, Rahul Kumar <rahulsinner@gmail.com> wrote:
Hi,
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?
The Enumerator.new uses Fibers internally. Check enumerator.c in the sources;
besides that, how would you achieve the needed effect without coroutines?
···
On Wed, 18 May 2011 20:12:43 +0900, Robert Klemme wrote:
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.
fib_gen = Enumerator.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}
Now you can do exactly the same as you did with your example
# 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 }
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.
fib_gen = Enumerator.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}
Now you can do exactly the same as you did with your example
# 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 }
For this Fiber would be the wrong tool.
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
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
}
}
# Find the fibonacci number greater than 1000
p fib_gen.find {|x| x > 1000 }
# take first 10 fibonacci numbers
p fib_gen.take 10
# take_while numbers are smaller than 1000
p fib_gen.take_while {|x| x < 1000 }
Kind regards
robert
···
On Wed, May 18, 2011 at 2:14 PM, Peter Zotov <whitequark@whitequark.org> wrote:
On Wed, 18 May 2011 20:12:43 +0900, Robert Klemme wrote:
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.
···
On Wed, May 18, 2011 at 8:50 AM, Rahul Kumar <rahulsinner@gmail.com> wrote:
On Wed, May 18, 2011 at 4:42 PM, Robert Klemme > <shortcutter@googlemail.com>wrote:
> If I understand correctly, writing generators was one of the purposes of
> fibers. Why can't fibers be made enumerable by default?
I am not sure how that interacts with the original intent of Fibers:
their main purpose is to bring coroutines to Ruby.