Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement headTail(): map (head, tailStream) to another stream #54

Closed
8 tasks done
amaembo opened this issue Jan 14, 2016 · 0 comments
Closed
8 tasks done

Implement headTail(): map (head, tailStream) to another stream #54

amaembo opened this issue Jan 14, 2016 · 0 comments
Milestone

Comments

@amaembo
Copy link
Owner

amaembo commented Jan 14, 2016

Extracted from #50 as it's substantially different feature.

The following method is proposed:

class StreamEx<T> {
    // Return StreamEx which is the result of applying the mapper function to the first
    // stream element and the stream of the rest elements; the mapper function is applied 
    // at most once during the terminal operation execution
    <U> StreamEx<U> headTail(BiFunction<T, StreamEx<T>, Stream<U>> mapper) { ... };

    // Supplier is called if this stream is empty
    <U> StreamEx<U> headTail(BiFunction<T, StreamEx<T>, Stream<U>> mapper, Supplier<Stream<U>> supplier) { ... };
}

Such method is really flexible (it allows to implement most of other intermediate operations). The drawback is that it cannot be parallelized well (only poor-man buffered parallelization could be performed) and recursive usage eats the stack (which could be partially solved if TSO can be implemented for some spliterators).

Usage examples.

Prime numbers:

static StreamEx<Integer> sieve(StreamEx<Integer> input) {
    return input.withFirst((head, tail) -> sieve(tail.filter(n -> n % head != 0))
                      .prepend(head));
}

sieve(StreamEx.iterate(2, x -> x+1)).takeWhile(x -> x < 10000).forEach(System.out::println);

Lazy scanLeft:

static <T> StreamEx<T> scanLeft(StreamEx<T> input, BinaryOperator<T> operator) {
    return input.headTail((head, tail) -> 
        scanLeft(tail.mapFirst(cur -> operator.apply(head, cur)), operator)
            .prepend(head));
}

Revert stream:

static <T> StreamEx<T> reverse(StreamEx<T> input) {
    return input.headTail((head, tail) -> reverse(tail).append(head));
}

takeWhile op implementation:

static <T> StreamEx<T> takeWhile(StreamEx<T> input, Predicate<T> predicate) {
    return input.headTail((head, tail) -> predicate.test(head) ? 
                 takeWhile(tail, predicate).prepend(head) : null );
}

takeWhileClosed (including the first violating element):

static <T> StreamEx<T> takeWhileClosed(StreamEx<T> input, Predicate<T> predicate) {
    return input.headTail((head, tail) -> predicate.test(head) ? 
            ? takeWhileClosed(tail, predicate).prepend(head)
            : Stream.of(head));
}

cycle - infinitely cycles the input, lazy

static <T> StreamEx<T> cycle(StreamEx<T> input) {
    return input.headTail((head, tail) -> cycle(tail.append(head)).prepend(head));
}

mirror - this stream, then reversed stream, also lazy

static <T> StreamEx<T> mirror(StreamEx<T> input) {
    return input.headTail((head, tail) -> mirror(tail).append(head).prepend(head));
}

every - take every nth element

static <T> StreamEx<T> every(StreamEx<T> input, int n) {
    return input.headTail((head, tail) -> every(tail.skip(n-1), n).prepend(head) );
}

the first stream element if nothing matches the predicate; the first matching otherwise

static <T> T firstMatchingOrFirst(StreamEx<T> stream, Predicate<T> predicate) {
    return stream.headTail(
          (head, tail) -> tail.prepend(head).filter(predicate).append(head))
                    .findFirst().get();
}

stream of singleton lists to stream of fixed-size batches

static <T> StreamEx<List<T>> batches(StreamEx<List<T>> input, int size) {
    return input.headTail((head, tail) -> head.size() >= size ? 
        batches(tail, size).prepend(head) : 
        batches(tail.mapFirst(next -> StreamEx.of(head, next).toFlatList(l -> l)), size));
}

stream of singleton lists to stream of sliding windows

static <T> StreamEx<List<T>> sliding(StreamEx<List<T>> input, int size) {
    return input.headTail((head, tail) -> head.size() == size ? 
        sliding(tail.mapFirst(next -> StreamEx.of(head.subList(1, size), next)
              .toFlatList(l -> l)), size).prepend(head) : 
        sliding(tail.mapFirst(next -> StreamEx.of(head, next).toFlatList(l -> l)), size));
}

Primitive specializations also could be implemented using BiFunction<Integer, IntStreamEx, IntStream> mapper, etc. (postponed)

  • Implementation
  • JavaDoc
  • TSO implementation (Tail-stream optimization)
  • TSO documentation
  • Tests
  • Examples
  • Changes
  • Cheatsheet
amaembo added a commit that referenced this issue Jan 14, 2016
amaembo added a commit that referenced this issue Jan 14, 2016
spliterators after tail call optimization
amaembo added a commit that referenced this issue Jan 15, 2016
amaembo added a commit that referenced this issue Jan 16, 2016
amaembo added a commit that referenced this issue Jan 17, 2016
sorted, mapFirst), batches now produces incomplete tail as well
amaembo added a commit that referenced this issue Jan 17, 2016
amaembo added a commit that referenced this issue Jan 18, 2016
@amaembo amaembo added this to the 0.5.3 milestone Jan 18, 2016
amaembo added a commit that referenced this issue Jan 18, 2016
@amaembo amaembo closed this as completed Jan 18, 2016
@amaembo amaembo mentioned this issue Jan 26, 2016
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant