The idea here is to produce a new stream for each individual length in the range [1 .. 2], and to flatten those streams into one single stream. flatMap() is essentially the same as a nested loop in imperative programming.
3. Combine letters in a cartesian product
This is the trickiest part: We need to combine each letter with each letter length times. For this, we’ll use the following stream:
We’re using again rangeClosed() to produce values in the range [1 .. length-1]. foldLeft() is the same as reduce(), except that foldLeft() is guaranteed to go from “left to right” in a stream, without requiring the folding function to be associative. Whew.
In other, more understandable words: foldLeft() is nothing else but an imperative loop. The “seed” of the loop, i.e. the loop’s initial value, is a complete alphabet (Seq.seq(alphabet)). Now, for every value in the range [1 .. length-1], we produce a cartesian product (crossJoin()) between the letters “folded” so far and a new alphabet, and we concatenate each combination into a single new string (t.v1 and t.v2).
That’s it!
Combining everything
The following simple program prints all the values from A .. Z, AA .. ZZ, AAA .. ZZZ to the console:
import java.util.List;
import org.jooq.lambda.Seq;
public class Test {
public static void main(String[] args) {
int max = 3;
List<String> alphabet = Seq
.rangeClosed('A', 'Z')
.map(Object::toString)
.toList();
Seq.rangeClosed(1, max)
.flatMap(length ->
Seq.rangeClosed(1, length - 1)
.foldLeft(Seq.seq(alphabet), (s, i) ->
s.crossJoin(Seq.seq(alphabet))
.map(t -> t.v1 + t.v2)))
.forEach(System.out::println);
}
}
I am sure that solution is basically equivalent to your code, but it does not utilize jOOλ and I think brings out the structure of the problem rather better. Also, it would parallelize.
Why do you say that the Java 8 Stream API does not offer enough functionality for this task? In fact, it does.
I describe a pure Java 8 solution, with some explanations, at http://sebastian-millies.blogspot.de/2015/09/cartesian-products-with-kleisli.html“.
I am sure that solution is basically equivalent to your code, but it does not utilize jOOλ and I think brings out the structure of the problem rather better. Also, it would parallelize.
— Sebastian
Hi Sebastian,
Yes, it was me :-)
Very very nice, thank you very much for this writeup. I stand correctend and I’ll study your insights. Keep it up,
Lukas
What I like in Kotlin that tasks like that can be really easy:
val letters = ‘a’..’z’
letters flatMap { letters map { char -> it.toString() + char }
Would you mind explaining? There seems to be a lot of implicit stuff there. Such as, what’s
char
and what’sit
?I’m a little late to the party but for the sake of completeness… using Javaslang we can do it like so in Java 8: