The model that Tagir implemented has a natural analogue: the for loop.  

    for (T v = seed; predicate.test(v); v = op.apply(v)) { … }

Like a for-loop, this proposed version of iterate() can generate an empty 
sequence; if the predicate applied to the seed returns false, you’ll get an 
empty stream.  

There are lots of for-loops that might want to be converted to streams, but 
don’t fit into the existing factories: 

    for (T p = root; p != null; p = p.next) { … }
converts naturally to
    Stream.iterate(root, p -> p != null; p -> p.next)…

Providing an initial value is not at odds with an empty sequence; it still gets 
tested by the predicate.  


On Feb 14, 2016, at 8:03 PM, Stuart Marks <stuart.ma...@oracle.com> wrote:

> Hi Tagir,
> 
> Thanks for picking this up.
> 
> I'll be at a conference this week and I won't have much time to discuss this 
> in detail until afterward. But here are some quick thoughts about the 
> proposal.
> 
> I'd suggest focusing on the API first before worrying about how to track the 
> stream state with booleans, etc. Is the API convenient to use, and how well 
> does it support the use cases we envision for it?
> 
> In particular, I can imagine a number of cases where it would be very helpful 
> to be able to support an empty stream, or where the computation to produce 
> the first element is the same as the computation to produce subsequent 
> elements. Requiring a value for the first stream element is at odds with that.
> 
> Here are some ideas for use cases to try out:
> 
> - a series of dice rolls representing a round of craps [1]
> - elements drawn from a queue until the queue is empty or until
>   a sentinel is reached
> - a sequence of numbers that (probably) terminates but whose length
>   isn't necessarily known in advance (e.g. Collatz sequence [2])
> 
> [1] https://en.wikipedia.org/wiki/Craps
> 
> [2] https://en.wikipedia.org/wiki/Collatz_conjecture
> 
> Note that in some cases the sentinel value that terminates the stream should 
> be part of the stream, and in other cases it's not.
> 
> I'm sure you can find more uses cases by perusing Stack Overflow. :-)
> 
> I'm a bit skeptical of the use of "iterate" for producing a finite stream. 
> There are the usual issues with overloading, but there's also potential 
> confusion as some forms of iterate() are infinite and others finite. I'll 
> suggest the name "produce" instead, but there are surely better terms.
> 
> One thing to think about is where the state of the producer is stored. Is it 
> expected to be in an argument that's passed to each invocation of the 
> functional argument, or is it expected to be captured? I don't think there's 
> an answer in isolation; examining use cases would probably shed some light 
> here.
> 
> Here are a few API ideas (wildcards elided):
> 
> --
> 
> <T> Stream<T> iterate(T seed, Predicate<T> predicate, UnaryOperator<T> f)
> 
> The API from your proposal, for comparison purposes.
> 
> --
> 
> <T> Stream<T> produce(Supplier<Optional<T>>)
> 
> Produces elements until empty Optional is returned. This box/unboxes every 
> element, maybe(?) alleviated by Valhalla.
> 
> --
> 
> <T> Stream<T> produce(BooleanSupplier, Supplier<T>)
> 
> Calls the BooleanSupplier; if true the next stream element is what's returned 
> by calling the Supplier. If BooleanSupplier returns false, end of stream. If 
> you have an iterator already, this enables
> 
> produce(iterator::hasNext, iterator::next)
> 
> But if you don't have an iterator already, coming up with the functions to 
> satisfy the iterator-style protocol is sometimes painful.
> 
> --
> 
> <T> Stream<T> produce(Predicate<Consumer<T>> advancer)
> 
> This has an odd signature, but the function is like Spliterator.tryAdvance(). 
> It must either call the consumer once and return true, or return false 
> without calling the consumer.
> 
> --
> 
> <T> Stream<T> produce(Consumer<Consumer<T>> advancer)
> 
> A variation of the above, without a boolean return. The advancer calls the 
> consumer one or more times to add elements to the stream. End of stream 
> occurs when the advancer doesn't call the consumer.
> 
> --
> 
> <T> Stream<T> produce(Supplier<Stream<T>>)
> 
> A variation of Supplier<Optional<T>> where the supplier returns a stream 
> containing zero or more elements. The stream terminates if the supplier 
> returns an empty stream. There "boxing" overhead here, but we don't seem to 
> be bothered by this with flatMap().
> 
> --
> 
> s'marks
> 
> 
> On 2/14/16 6:53 AM, Tagir F. Valeev wrote:
>> Hello!
>> 
>> I wanted to work on foldLeft, but Brian asked me to take this issue
>> instead. So here's webrev:
>> http://cr.openjdk.java.net/~tvaleev/webrev/8072727/r1/
>> 
>> I don't like iterator-based Stream source implementations, so I made
>> them AbstractSpliterator-based. I also implemented manually
>> forEachRemaining as, I believe, this improves the performance in
>> non-short-circuiting cases.
>> 
>> I also decided to keep two flags (started and finished) to track the
>> state. Currently existing implementation of infinite iterate() does
>> not use started flag, but instead reads one element ahead for
>> primitive streams. This seems wrong to me and may even lead to
>> unexpected exceptions (*). I could get rid of "started" flag for
>> Stream.iterate() using Streams.NONE, but this would make object
>> implementation different from primitive implementations. It would also
>> be possible to keep single three-state variable (byte or int,
>> NOT_STARTED, STARTED, FINISHED), but I doubt that this would improve
>> the performance or footprint. Having two flags looks more readable to
>> me.
>> 
>> Currently existing two-arg iterate methods can now be expressed as a
>> partial case of the new method:
>> 
>> public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
>>     return iterate(seed, x -> true, f);
>> }
>> (same for primitive streams). I may do this if you think it's
>> reasonable.
>> 
>> I created new test class and added new iterate sources to existing
>> data providers.
>> 
>> Please review and sponsor!
>> 
>> With best regards,
>> Tagir Valeev.
>> 
>> (*) Consider the following code:
>> 
>> int[] data = {1,2,3,4,-1};
>> IntStream.iterate(0, x -> data[x])
>>          .takeWhile(x -> x >= 0)
>>          .forEach(System.out::println);
>> 
>> Currently this unexpectedly throws an AIOOBE, because
>> IntStream.iterate unnecessarily tries to read one element ahead.
>> 

Reply via email to