Making a Fibonator with Generators in ES6, Python and Ruby
Fibonacci numbers are beautiful, this sequence appears in various forms, from data structures to sunflower seeds. Let’s see how we can make a Fibonator - a generator which returns Fibonacci sequence.
With ES6, JavaScript finally got generators, while in Python they have been available long time ago. In Python, yield keyword converts function to generator:
In ES6, yield keyword is not enough, function keyword has to be followed by asterisk (function*) to make function a generator:
Ruby also have yield keyword, but it means something different than in ES6 and Python. In Ruby, yield calls a code block associated with the method which contains that yield. Here is silly example:
There is no encoded state machine here, we cannot print one number, stop and then resume, so clearly this is not a generator. To get generator in Ruby we need to use Enumerator class, where yielder (which is instance of Enumerator::Yielder) is given as block parameter. Now we can call yield method (do not confuse with yield keyword). Here is fibonator using Enumerator:
Confusing part is yield method, which share the name with yield keyword. Alternitavley, Enumerator class provide « method, so we can replace line “yielder.yield(a)” with “yielder « a”, bit I find this even more confusing, so I just stick with yield method. Internally, state machine is implemented using Fibers.
Now, once we have fibonator, we can call next method to print sequence manually
Or using iterator methods. After finishing, these methods reset generator state back to initial state (as we expect):
Using iteration methods, it is easy to make endless loop. For example, let’s say we want to find first 10 Fibonacci sequence numbers which divides by 5:
This will never return, as select will happily draw number from fibonator, which produces infinite sequence. To avoid this, we need a lazy iteration method.
Lazy enumeration forces first method to draw numbers, so in this case sequence works as expected. There is a nice overview about lazy Ruby features in this blog post.