Some examples from Python: https://www.programiz.com/python-programming/generator
Some examples using plain Java 8: https://www.mguenther.net/2016/01/functional_generators_in_java_8/index.html
I am a little confused why this is regarded as a powerful pattern. It seems I can implement the same using a normal `Iterator`. For instance, from the Python example:
class PowTwoGen {
final int max;
int n = 0;
PowTwoGen(int max) { this.max = max;}
boolean hasNext() { return n <= max;}
int next() { return Math.pow(2, n++); }
}
The examples for the Java 8 article can also be done with a simple method call but instead the examples with the Generator seems overly complex. What am I missing?
Somebody could make the case that returning a sentinel value or an exception is a better API since there is no risk somebody else is going to call the next() method after you call hasNext() and next(). Writing a generator that wraps a generator is a little simpler than writing an interest or that wraps an iteration because you don’t have to write a hasNext() function, which can occasionally be awkward.
That generator library has a few functions, like map that work on generators, unfortunately the Java stdlib doesn’t come with anything like that. (There is the streams API but it is over-complicated.)
I’ll point out this library I wrote
https://github.com/paulhoule/pidove
which does a lot of what the Steams library does but it works on iterators without creating streams. If you like those generator examples you might like pidove.
As for Python it is kinda accidental that generators would up related to coroutines, that is, generators were an easy way to implement coroutines, later async/await and stuff like that got added.
Your example in Python:
class Pow2:
def __init__(self, n):
self.value = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.value < self.n:
ans = self.value * self.value
self.value += 1
return ans
else:
raise StopIteration
iter(Pow2(n))
is semantically equivalent to def pow2(n):
value = 0
while value < n:
yield value * value
value += 1
pow2(n)
and (i * i for i in range(n))
but both the generators are a lot less code.The generator expression (last one) can actually be implemented right now in Java streams (something like `IntRange::to(n).map(i => i * i)`). But the generator functions which use `yield` are necessary for more complex expressions: the ones which have nested loops, resources, exceptions, etc. which are even more verbose when converted into generator classes
The power of the generator is that, unlike an iterator, the collection of values doesn't "exist", it is created on the fly. This comes in to play when iterating over, say the set of integers, or something else that would take significant amounts of memory.
Also, as functions, generators can be passed around in any language that treats functions a first-class types.