maidh91 / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

RFE: Add support for Ranges #322

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
I'd like to see a simple `count(n)` method in `Iterables`(and probably in
`Iterators`, too). This would allow to easily generate a range of numbers
in a memory-efficient way (i.e. without the need to keep the whole range in
memory).

E.g., `count(20)` would generate the numbers 20, 21, 22, 23, and so on. A
default starting value (I'd expect `0`) is conceivable, though not really
necessary.

If there is a much better way to achieve the above and which I might have
missed, please enlighten me.

Original issue reported on code.google.com by j...@nwsnet.de on 3 Feb 2010 at 9:29

GoogleCodeExporter commented 9 years ago
I would put it in Ints/Longs rather than Iterables/Iterators.

Original comment by fin...@gmail.com on 3 Feb 2010 at 9:37

GoogleCodeExporter commented 9 years ago
`Suppliers.count()` (returning an instance of `Supplier<Integer>` and/or
`Supplier<Long>`; maybe the latter would be sufficient?) might be a conceivable 
place
for it, too.

Original comment by j...@nwsnet.de on 8 Feb 2010 at 9:34

GoogleCodeExporter commented 9 years ago
This is one of the things our forthcoming Range stuff will support.  There may 
still 
be value in a "shortcut" method like this in Itera*s, but we'll see at the 
time, and 
it wouldn't be called "count" (since I expected that to mean "count the number 
of 
elements left in the iteration).

Original comment by kevinb@google.com on 12 Feb 2010 at 5:08

GoogleCodeExporter commented 9 years ago
As long as these ranges are `Iterable`s (and are lazy) and support an "open 
end",
I'll be fine.

Original comment by j...@nwsnet.de on 14 Feb 2010 at 5:23

GoogleCodeExporter commented 9 years ago
Any news regarding the promised Range stuff?

Original comment by j...@nwsnet.de on 18 Aug 2010 at 1:08

GoogleCodeExporter commented 9 years ago
Issue 506 has been merged into this issue.

Original comment by boppenh...@google.com on 30 Dec 2010 at 1:21

GoogleCodeExporter commented 9 years ago

Original comment by boppenh...@google.com on 30 Dec 2010 at 1:22

GoogleCodeExporter commented 9 years ago

Original comment by fry@google.com on 26 Jan 2011 at 10:40

GoogleCodeExporter commented 9 years ago

Original comment by kevinb@google.com on 27 Jan 2011 at 6:16

GoogleCodeExporter commented 9 years ago
Issue 429 has been merged into this issue.

Original comment by kevinb@google.com on 27 Jan 2011 at 6:17

GoogleCodeExporter commented 9 years ago
Any news regarding this? Having ranges would also allow simplifying or even 
replacing some of those ugly `for` loops.

Original comment by j...@nwsnet.de on 31 Mar 2011 at 12:06

GoogleCodeExporter commented 9 years ago

Original comment by kevinb@google.com on 8 Apr 2011 at 2:06

GoogleCodeExporter commented 9 years ago

Original comment by kevinb@google.com on 3 May 2011 at 10:48

GoogleCodeExporter commented 9 years ago
How is this class meant to be used? I see neither a public constructor nor a 
public factory method nor classes like `Ints` implement methods that would 
yield a range.

Oh, and is `apply(C)` going to be deprecated forever and right from the start 
because the name inherited from `Predicate` is ambiguous for ranges?

Original comment by j...@nwsnet.de on 5 May 2011 at 12:46

GoogleCodeExporter commented 9 years ago
You use the Ranges class to create Ranges. And yes, I imagine apply(C) is going 
to be deprecated forever because you should always use the more descriptive 
contains(C) when calling a method directly. Only code treating the Range as a 
Predicate should be using apply.

Original comment by cgdec...@gmail.com on 5 May 2011 at 2:16

GoogleCodeExporter commented 9 years ago
We treat the meaning of @Deprecated as:  "there is no good reason that your 
code should invoke this method (directly)."  It's a common misconception that 
deprecation is only about "I'm going to delete this."  Ordering.from(Ordering) 
is another example of a deprecated method we expect to have forever.

Original comment by kevinb@google.com on 5 May 2011 at 2:31

GoogleCodeExporter commented 9 years ago
Oh, I totally forgot about `Ranges` while taking a deeper look into `Ranges` 
and its Javadocs.

But I still can't imagine how this can be used to solve my initially (though 
maybe not sufficiently explicitly) described use case of iterating over ranges 
of numbers. I'd expect something like `for (Integer x : Ranges.closed(1, 100))` 
to work, as a cleaner and less error-prone alternative to incrementing an int 
in a `for` loop, and similar to Scala's `1 to 100` or Python's `range(1, 101)`, 
for example.

`Ranges.atLeast(20)` resembles my above `count(20)` example, but I still need 
to be able to iterate over it for as long as I decide not to break the loop 
over it (which, as I'd expect, has to be ultimately done with open ranges 
anyway).

What did I miss this time? I guess it doesn't implement `Iterable` or something 
like that for a reason, including the above one about not terminating by itself 
in case of (half-)open ranges.

Original comment by j...@nwsnet.de on 6 May 2011 at 9:09

GoogleCodeExporter commented 9 years ago
Indeed, that is somehow unexpected.

It looks like the automated generation of numbers isn't supported by Range, 
which is the main reason why I starred the bug: a python-like range().

Current implementation of Range is interesting but much less than the initial 
request that yo...@nwsnet.de initially asked.

Original comment by ogregoire on 6 May 2011 at 11:56

GoogleCodeExporter commented 9 years ago
For this, you need to combine a Range with a DiscreteDomain:

for (int i : Ranges.closed(1, 100).asSet(DiscreteDomains.integers()))

Original comment by nev...@gmail.com on 6 May 2011 at 12:17

GoogleCodeExporter commented 9 years ago
@nev..., I believe the initial request was that there would be an iterator that 
would generate the next number on-the-fly ("in a memory-efficient way"), rather 
than "keep the whole range in memory".

Personally, I don't think this would be a particularly complicated thing to 
implement yourself, and I don't know how much it would really add for general 
use (though that's not my call).

Original comment by raymond....@gmail.com on 6 May 2011 at 1:14

GoogleCodeExporter commented 9 years ago
@raymond: The ImmutableSet returned by asSet(DiscreteDomain) (note that it 
isn't called "toSet") *does* generate numbers on the fly in a memory-efficient 
manner.

Original comment by cgdec...@gmail.com on 6 May 2011 at 1:44

GoogleCodeExporter commented 9 years ago
My mistake--very well then :)

Original comment by raymond....@gmail.com on 6 May 2011 at 5:29

GoogleCodeExporter commented 9 years ago
As you can tell from

for (int i : Ranges.closed(1, 100).asSet(DiscreteDomains.integers()))

it is clearly not a design goal of Ranges to support this use case cleanly.  
Why?  Because even if we did the best we could to support it, it would *still* 
be better to do this:

for (int i = 1; i <= 100; i++)

Original comment by kevinb@google.com on 6 May 2011 at 11:11

GoogleCodeExporter commented 9 years ago
Well, the for loop is not the only use case. Personally, I won't use that much. 
I really wanted Ranges to implement Iterable in order to easily create data 
structure of numbers that follow a simple rule, transform the lists, filter 
them, etc.

Looks like I'll be stuck with the ugly asSet(DiscreteDomain.integers()), which 
vocabulary is absolutely correct, but not understandable for the common of 
developers, especially non-native English speakers.

Original comment by ogregoire on 7 May 2011 at 6:28

GoogleCodeExporter commented 9 years ago
Hm, bummer.

The discrete domain stuff is far too complicated for being used commonly. Also, 
while `asSet` is returning an immutable set with a fixed iteration order, it is 
irritating as a reader would expect a range to return some kind of list or 
another iterable that is not looking as it wouldn't care about ordering.

My issue with counting `for` loops is they are hard to grok *in detail* at a 
quick glance and thus easily hide off-by-one errors. ("Modern") "scripting" 
languages, e.g. Ruby, became really popular for a reason; clean ways to create 
ranges is one tiny part of the story.

I'd happily add some chars to the `for` one-liner in order to gain clarity.

A counting iterator indeed is easy to implement, but it still could be used in 
lots of places even in smaller projects. And there are various number types 
this could be useful for (int, long, BigDecimal), too, which would require 
additional implementations. That's why I think a library should contain these.

In any case, even with the code in comment #19, I don't consider my original 
request fixed.

However, regarding the issue, I can imagine the addition of an `IntRange` 
subclass which would provide such functionality. But that's just a quick 
thought.

Original comment by j...@nwsnet.de on 9 May 2011 at 10:15

GoogleCodeExporter commented 9 years ago
By far the most common Range->Set usage for me would be to generate an integer 
set.  So I think it would be a nice bit of syntactic sugar to provide as 
asIntegerSet() method which simply uses asSet(DiscreteDomains.integers()) 
behind the scenes.  Obviously the user can write the method themselves but this 
way it's explicitly provided in the direct API instead of specified by the user 
in an indirect way.

Also it's too bad that only continuous ranges were provided.  R-trees or 
inversion lists would have been more generally useful.

Original comment by ted.zlat...@gmail.com on 9 May 2011 at 2:20

GoogleCodeExporter commented 9 years ago
> Also, while `asSet` is returning an immutable set with a fixed iteration 
order, it is irritating as a reader would expect a range to return some kind of 
list or another iterable that is not looking as it wouldn't care about ordering.

It returns an ImmutableSortedSet, so the fact that it's ordered should be clear.

> So I think it would be a nice bit of syntactic sugar to provide as 
asIntegerSet() method which simply uses asSet(DiscreteDomains.integers()) 
behind the scenes.

It doesn't make sense to have an asIntegerSet() method on a Range<Foo>, were it 
even possible.

Original comment by cgdec...@gmail.com on 9 May 2011 at 10:01

GoogleCodeExporter commented 9 years ago
> It doesn't make sense to have an asIntegerSet() method on a Range<Foo>, were 
it even possible.

I think it makes sense for any Range<Number> or at least any numbers that can 
be mapped on the number line (forgive my ignorance of all the Number variations 
in Java).

Original comment by ted.zlat...@gmail.com on 10 May 2011 at 1:26

GoogleCodeExporter commented 9 years ago
My point was that Range is a general-purpose class that can be used with many 
things that aren't numbers. In any case, I think using static imports might 
make this read a little more like you want: range.asSet(integers())

Original comment by cgdec...@gmail.com on 10 May 2011 at 2:19

GoogleCodeExporter commented 9 years ago
I think this issue was about creating a convenient method for iterating. The 
proposed Range is more about mathematical notion of intervals. I think there 
already is such a feature - it is the AbstractLinkedIterator.
I created an Iterable around it, which takes a first value and a function for 
computing the next value. I added static factory method for this Iterable, 
which I call unfold.

Examples:

    @Test
    public final void numsFrom() {
        assertThat(limit(unfold(5, successor), 3), hasItems(5, 6, 7));
    }

    @Test
    public final void squares() {
        final Iterable<Integer> squares = transform(unfold(0, successor),
                new Function<Integer, Integer>() {
                    @Override
                    public Integer apply(final Integer input) {
                        return input * input;
                    }
                });
        assertThat(limit(squares, 6), hasItems(0, 1, 4, 9, 16, 25));
    }

    @Test
    public final void primes() {
        final Iterable<Integer> primes = unfold(2, new Function<Integer, Integer>() {
            private Predicate<Integer> sieve = alwaysTrue();

            @Override
            public Integer apply(Integer input) {
                while (!sieve.apply(input)) {
                    input++;
                }
                final Integer prime = input;
                sieve = and(sieve, new Predicate<Integer>() {
                    @Override
                    public boolean apply(final Integer number) {
                        return number % prime != 0;
                    }
                });
                return prime;
            }
        });
        assertThat(get(primes, 300), is(1987));
    }

I think this might be a better temporary solution until it is clear what sort 
of Ranges people really want.

Original comment by gscerbak@gmail.com on 10 May 2011 at 12:08

GoogleCodeExporter commented 9 years ago
Should I make a request for a NumberRange class?  The specific case where the 
range is numeric has specific uses and optimizations that can't apply to 
general Ranges.

Original comment by ted.zlat...@gmail.com on 12 May 2011 at 2:37

GoogleCodeExporter commented 9 years ago
I don't think NumberRange is a good idea. The thing that people are asking for 
is Range for DiscreteDomain, especially with convenient Iterables. It looks 
similar to IndexedIterable, which is in the idea graveyard, but I don't know 
for what reasons. As AbstractLinkedIterator got into the library, it could be a 
viable alternative to implement something similar to such Iterable with it, as 
I showed above.

Original comment by gscerbak@gmail.com on 12 May 2011 at 2:53

GoogleCodeExporter commented 9 years ago
OK, I'll go with that since it's so easy to use Range as a NumberRange.  Thanks.

Original comment by ted.zlat...@gmail.com on 12 May 2011 at 3:05

GoogleCodeExporter commented 9 years ago
I think the ranges is an excellent addition to the library. All new language or 
library additions will be hard to grok at first. Anyways, I had a small 
question regarding ranges:

I previously implemented some utilities to handle Ranges (intervals in our 
system).
For simplicity sake, we forced intervals to have a closed left and
open right. We stumbled a bit when we wanted to decide whether an open
interval contains another open interval when the bounds are equal (eg.
[0, 1) and [0, 1)). As far as I know (I'm not a mathematician), this
is only true when the measures of both intervals are equal, which
cannot be guaranteed. Therefor, I opted to have it return false, but I
was wondering why Guava decided to return true instead. 

With kind regards,

Original comment by g.e.dej...@gmail.com on 6 Jun 2011 at 12:50

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:16

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:10