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

RFE: Add support for Ranges #322

Closed
gissuebot opened this issue Oct 31, 2014 · 34 comments
Closed

RFE: Add support for Ranges #322

gissuebot opened this issue Oct 31, 2014 · 34 comments
Labels
status=fixed type=enhancement Make an existing feature better

Comments

@gissuebot
Copy link

gissuebot commented Oct 31, 2014

Original issue created by j...@nwsnet.de on 2010-02-03 at 09:29 AM


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.

@gissuebot
Copy link
Author

Original comment posted by finnw1 on 2010-02-03 at 09:37 AM


I would put it in Ints/Longs rather than Iterables/Iterators.

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by j...@nwsnet.de on 2010-02-08 at 09:34 AM


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.

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2010-02-12 at 05:08 PM


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).

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by j...@nwsnet.de on 2010-02-14 at 05:23 PM


As long as these ranges are Iterables (and are lazy) and support an "open end",
I'll be fine.

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by j...@nwsnet.de on 2010-08-18 at 01:08 PM


Any news regarding the promised Range stuff?

@gissuebot
Copy link
Author

Original comment posted by boppenheim@google.com on 2010-12-30 at 01:21 AM


Issue #506 has been merged into this issue.

@gissuebot gissuebot mentioned this issue Oct 31, 2014
@gissuebot
Copy link
Author

Original comment posted by boppenheim@google.com on 2010-12-30 at 01:22 AM


(No comment entered for this change.)

@gissuebot
Copy link
Author

Original comment posted by fry@google.com on 2011-01-26 at 10:40 PM


(No comment entered for this change.)


Status: Accepted
Labels: Type-Enhancement

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-01-27 at 06:16 AM


(No comment entered for this change.)


Status: Started

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-01-27 at 06:17 AM


Issue #429 has been merged into this issue.

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by j...@nwsnet.de on 2011-03-31 at 12:06 PM


Any news regarding this? Having ranges would also allow simplifying or even replacing some of those ugly for loops.

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-04-08 at 02:06 AM


(No comment entered for this change.)


Labels: Milestone-Release10

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-05-03 at 10:48 PM


(No comment entered for this change.)


Status: Fixed

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by j...@nwsnet.de on 2011-05-05 at 12:46 PM


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?

@gissuebot
Copy link
Author

Original comment posted by cgdecker on 2011-05-05 at 02:16 PM


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.

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-05-05 at 02:31 PM


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.

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by j...@nwsnet.de on 2011-05-06 at 09:09 AM


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.

@gissuebot
Copy link
Author

Original comment posted by ogregoire on 2011-05-06 at 11:56 AM


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.

@gissuebot
Copy link
Author

Original comment posted by neveue on 2011-05-06 at 12:17 PM


For this, you need to combine a Range with a DiscreteDomain:

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

@gissuebot
Copy link
Author

Original comment posted by raymond.rishty on 2011-05-06 at 01:14 PM


@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).

@gissuebot
Copy link
Author

Original comment posted by cgdecker on 2011-05-06 at 01:44 PM


@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.

@gissuebot
Copy link
Author

Original comment posted by raymond.rishty on 2011-05-06 at 05:29 PM


My mistake--very well then :)

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-05-06 at 11:11 PM


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++)

@gissuebot
Copy link
Author

Original comment posted by ogregoire on 2011-05-07 at 06:28 PM


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.

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by j...@nwsnet.de on 2011-05-09 at 10:15 AM


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.

@gissuebot
Copy link
Author

Original comment posted by ted.zlatanov on 2011-05-09 at 02:20 PM


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.

@gissuebot
Copy link
Author

Original comment posted by cgdecker on 2011-05-09 at 10:01 PM


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.

@gissuebot
Copy link
Author

Original comment posted by ted.zlatanov on 2011-05-10 at 01:26 AM


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).

@gissuebot
Copy link
Author

Original comment posted by cgdecker on 2011-05-10 at 02:19 AM


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())

@gissuebot
Copy link
Author

Original comment posted by gscerbak on 2011-05-10 at 12:08 PM


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.

@gissuebot
Copy link
Author

Original comment posted by ted.zlatanov on 2011-05-12 at 02:37 PM


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.

@gissuebot
Copy link
Author

Original comment posted by gscerbak on 2011-05-12 at 02:53 PM


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.

@gissuebot
Copy link
Author

Original comment posted by ted.zlatanov on 2011-05-12 at 03:05 PM


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

@gissuebot
Copy link
Author

Original comment posted by g.e.dejong on 2011-06-06 at 12:50 PM


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,

@gissuebot gissuebot added status=fixed migrated type=enhancement Make an existing feature better labels Oct 31, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status=fixed type=enhancement Make an existing feature better
Projects
None yet
Development

No branches or pull requests

1 participant