Ruby Range issues

Since I haven’t posted to this group in the past 5 years or so, let me
start by saying how much I enjoy using Ruby! My comments below reflect
my desire to see a couple of small issues improved in this extraordinary
language!

The first is a requested feature (which works in 1.8.7 but not in
1.9.2p136), while the second appears to me to be a bug that exists in
both 1.8.7 and 1.9.2:

  1. Range.step says (per PickAxe 3) that it works with range elements
    that support .succ, or with numbers. It would be nice to have it work
    with elements that support .succ, /or with elements that support adding
    a number to them/. This would allow stepping through time ranges, for
    example.

  2. Range.step where the end is specified to be excluded ( ‘…’) on
    floats seems to exclude the last value that should be included. For
    example:
    ( 1.0 … 5.01).step( 2) {|x| puts x}
    outputs 1.0 and 3.0 but not 5.0.
    Interestingly, ( 1.0 … 5.01).cover?( 5.0) returns true, as it
    should!

Ray B.
**

On Tue, Feb 1, 2011 at 3:33 PM, Ray B. [email protected] wrote:

support .succ, or with numbers. It would be nice to have it work with
elements that support .succ, /or with elements that support adding a number
to them/. This would allow stepping through time ranges, for example.

Not sure this is a good idea. After all, if +1 would be the “default”
increment for a class #succ would have been implemented that way. In
absence of a #succ implementation a Range cannot do any assumption as
to how get the next value - that’s the exact reason why there is a
method #succ. You could as well wrap a Time instance in something
that implements #succ accordingly.

irb(main):001:0> require ‘delegate’
=> true
irb(main):002:0> class TD < SimpleDelegator
irb(main):003:1> def succ;self.class.new(getobj + 1); end
irb(main):004:1> end
=> nil
irb(main):005:0> t = TD.new(Time.now)
=> 2011-02-01 16:23:24 +0100
irb(main):006:0> t.succ
=> 2011-02-01 16:23:25 +0100
irb(main):007:0> t.succ.succ
=> 2011-02-01 16:23:26 +0100

  1. Range.step where the end is specified to be excluded ( ‘…’) on floats
    seems to exclude the last value that should be included. For example:
    ( 1.0 … 5.01).step( 2) {|x| puts x}
    outputs 1.0 and 3.0 but not 5.0.
    Interestingly, ( 1.0 … 5.01).cover?( 5.0) returns true, as it should!

Sounds like a bug which is not present in 1.9.2:

irb(main):008:0> (1.0…5.1).step(2).to_a
=> [1.0, 3.0, 5.0]
irb(main):009:0> (1.0…5.01).step(2).to_a
=> [1.0, 3.0, 5.0]

Cheers

robert

Thanks for the comments, Robert.

I agree that .succ for a Time is not obvious (there actually is a
.succ
method for Time in 1.9.2 although it reports that it is deprecated), but
I’d
still like to be able to use a specific step value. It may be that your
wrapping suggestion is the best way forward, but I would think a lot of
people might want to be able to step through time intervals.

On the second issue, you only show the results with two dots. What I
was
concerned about was when you use three dots in order to exclude the
endpoint
of the range. In both 1.8.7 and 1.9.2p136 you only get 1.0 and 3.0. It
appears that it first converts the end points to Integer before doing
the
end comparison. Here’s an example in 1.9.2 showing both the 2 dot and
the 3
dot versions:

irb
ruby-1.9.2-p136 :001 > ( 1.0 … 5.01).step(2).to_a
=> [1.0, 3.0, 5.0]
ruby-1.9.2-p136 :002 > ( 1.0 … 5.01).step(2).to_a
=> [1.0, 3.0]

The logic is in ruby_float_step in float.c.

It doesn’t do the obvious (add step to base until exceed maximum),
probably to avoid accumulating errors. But I think the logic is wrong
for the … case.

        if (!excl) n++;
        for (i=0; i<n; i++) {
            rb_yield(DBL2NUM(i*unit+beg));
        }