2 kyu

Generator Functions

Description:

In this kata, you'll become a functional programmer! You'll be implementing JavaScript-like generator functions in Java.

As a Java programmer, you might now be asking, "what's a generator function?". In essence, a generator function is simply a function whose execution can be paused at certain times. This pause is caused by the yield statement, which is similar to return in that it gives a value back to the caller. The yield statement, however, instead of exiting the function, causes it to suspend until the next time a value is requested. For example, this is what a generator function that returns the counting numbers might look like in JavaScript:

function* countingNumbers() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

Fairly counterintuitive, right? Why would a programmer ever purposefully enter an infinite loop with no means of breaking? In reality, this is a perfectly sane algorithm; think of it like an iterator of sorts that begets an arbitrarily large subset of the counting numbers. Let's look at that in action, once again in JavaScript:

let generator = countingNumbers();
console.log(generator.next()); // 0
console.log(generator.next()); // 1
console.log(generator.next()); // 2

What's really going on here? When we call countingNumbers() in that first line, we create a generator object by calling the generator function. Execution hasn't begun yet! Then, once we call generator.next(), the function runs until it reaches the first yield, at which point it suspends and returns the yielded value to the caller of next(). The next time we call next(), the function will unsuspend and once again run until the next yield. In this way, we have a sort of lazy supplier of the counting numbers.

JavaScript, for some reason, also has a yield*, or yield-from statement. When this is called with another generator function as a parameter, the currently running generator function begins delegating all calls to its next() function to the generator generated from the yield* statement. Let's see this in action:

function* threeFourFive() {
  yield 3;
  yield 4;
  yield 5;
}
function* oneThroughSeven() {
  yield 1;
  yield 2;
  yield* threeFourFive();
  yield 6;
  yield 7;
}
let generator = oneThroughSeven();
for (let i = 0; i < 7; i++) {
  console.log(generator.next()); // prints the numbers 1..7
}

Wow! Magical!

You can also pass parameters to next(), and this will cause the yield statement to take on that value. To demonstrate, here's a generator function that sums up the numbers passed to it:

function* summation(initialValue) {
  let total = initialValue;
  while (true) {
    total += yield total;
  }
}

When we call this generator's next() function for the first time, the parameter we pass to next() is passed to the function in the form of the argument initialValue. We can pass it more numbers in subsequent next() calls, which will then be added to total:

let gen = summation();
console.log(gen.next(10)); // 10
console.log(gen.next(42)); // 52
console.log(gen.next(17)); // 69
console.log(gen.next(638)); // 707

...and so on.

Now here's your task: you'll have to implement a GeneratorFunction<I, O> class that performs the same function as a JavaScript generator function using some Java sorcery! For example, here's what the above summation function will look like when you're done:

GeneratorFunction<Integer, Integer> summation = new GeneratorFunction<>(initialValue -> {
  int total = initialValue;
  while (true) {
    total += Flow.<Integer, Integer>yield(total);
  }
});
Generator<Integer, Integer> gen = summation.call();
System.out.println(gen.next(10)); // 10
System.out.println(gen.next(42)); // 52
System.out.println(gen.next(17)); // 69
System.out.println(gen.next(638)); // 707

...and so on. You'll also have to implement yield-from, which will look like this:

GeneratorFunction<Void, Integer> threeFourFive = new GeneratorFunction<>(() -> {
  Flow.yield(3);
  Flow.yield(4);
  Flow.yield(5);
});
GeneratorFunction<Void, Integer> oneThroughSeven = new GeneratorFunction<>(() -> {
  Flow.yield(1);
  Flow.yield(2);
  Flow.yieldFrom(threeFourFive);
  Flow.yield(6);
  Flow.yield(7);
});
Generator<Void, Integer> gen = oneThroughSeven.call();
for (int i = 0; i < 7; i++) {
  System.out.println(gen.next()); // prints the numbers 1..7
}

If Generator#next() is called when no data is left to be yielded (i.e. the function has finished executing and no data is buffered for yielding), null should be returned. Additionally, a Generator#done() -> boolean function should be written such that it returns whether the function has finished executing yet or not. Note that generators might also yield null as a value! In that case, you'll have to use done() to distinguish between generators that have yielded null and generators that are done.

JavaDoc comments exist in the initial solution to help you a bit. Good luck!

(Note: generator functions as presented in this problem slightly diverge from generator functions in actual JS, but it's a little simpler this way.)

Iterators
Functional Programming
Fundamentals

Stats:

CreatedOct 18, 2017
PublishedOct 19, 2017
Warriors Trained430
Total Skips102
Total Code Submissions167
Total Times Completed24
Java Completions24
Total Stars24
% of votes with a positive feedback rating96% of 12
Total "Very Satisfied" Votes11
Total "Somewhat Satisfied" Votes1
Total "Not Satisfied" Votes0
Total Rank Assessments8
Average Assessed Rank
2 kyu
Highest Assessed Rank
1 kyu
Lowest Assessed Rank
3 kyu
Ad
Contributors
  • phantamanta44 Avatar
  • kazk Avatar
  • Krzem5 Avatar
  • dfhwze Avatar
Ad