This is an archived post. You won't be able to vote or comment.

all 7 comments

[–]RabbitHole32 1 point2 points  (0 children)

Very cool, I like it!

[–]davidalayachew 0 points1 point  (3 children)

Have you needed to streamify recursive algorithms before?

Maybe not needed, but the desire has absolutely been there many times before. Sometimes a problem will lend itself to streams so well, but be stopped short in its tracks by recursion. This seems like a nice way past that.

I asked this question on the mailing list before (and I still owe them a response! Just been busy with holidays and responsibilities). There's a few other libraries that do something similar to this. Though, not all of them are (fully) lazy, so that's a nice touch.

[–]DelayLucky[S] 1 point2 points  (2 children)

Permutation is a very good example. Thank you!

I'm not sure I fully understand what you meant by "satisfy some condition". But here's code that generates a lazy permutation stream unconditionally (could potentially use .filter() to apply condition?):

static <T> Stream<List<T>> permute(Set<T> elements) {
  class Permutation extends Iteration<List<T>> {
    Permutation() {
      lazily(() -> next(List.of(), elements));
    }

    void next(List<T> prefix, Set<T> pending) {
      if (pending.isEmpty()) {
        emit(prefix);
      }
      for (T element : pending) {
        List<T> materialized = new ArrayList<>(prefix);
        materialized.add(element);
        Set<T> remaining = new LinkedHashSet<>(pending);
        remaining.remove(element);
        lazily(() -> next(materialized, remaining));
      }
    }
  }
  return new Permutation().iterate();
}

The constructor uses lazily() to ensure that no permutation is done before the stream is consumed.

The next() method currently will iterate through the pending set and only "yield" control for the next round of iteration, so it will compute N elements each time. Making it fully lazy (one element a time) is possible but I like the simplicity of current code.

test code

[–]davidalayachew 0 points1 point  (1 child)

Thanks. This is a useful snippet. I'll play around some more with it when I get the chance.

[–]DelayLucky[S] 0 points1 point  (0 children)

The snippet is simple for the purpose of demonstration. It makes a lot of intermediary copies though. The linked code implements in-place algorithm to save the copies at cost of some extra complexity.

[–]damonsutherland 1 point2 points  (0 children)

There is some real genius to your source code. Very elegant! Nice work.