Decreasing range?

Hello everybody.

I just noticed a behavior I don’t really get related to Range and
Enumerator.

if I write down something like (1…4).each { |i| p i } , this will print
1234
However, if I write (4…1).each { |i| p i }, nothing will be printed out
despite 4…1 is a valid range.

Can anyone explain me why this strange behavior is implemented as such,
and how can I circle that without the need to use the very ugly
1…4).each { |i| p (4-i) } ?

Thanks a lot for your answer,

Best regards

On Tue, Feb 1, 2011 at 12:51 AM, Stefano G.
[email protected]wrote:

Can anyone explain me why this strange behavior is implemented as such,
and how can I circle that without the need to use the very ugly
1…4).each { |i| p (4-i) } ?

4.downto(1) { |i| p i }

On Tue, Feb 1, 2011 at 8:01 AM, Josh C. [email protected] wrote:

On Tue, Feb 1, 2011 at 12:51 AM, Stefano G. [email protected]wrote:

Can anyone explain me why this strange behavior is implemented as such,
and how can I circle that without the need to use the very ugly
1…4).each { |i| p (4-i) } ?

4.downto(1) { |i| p i }

This is certainly the most efficient variant. For the general case
there is Enumerable#reverse_each - at least from 1.8.7 on. Since it’s
implemented in Range as well I guess this will be efficient, too.

Kind regards

robert

Thank you both, I feel ashamed that I didn’t think about “downto” …
Concerning my more general question, do you have any clue about why
decreasing ranges behave the way they do?

Thank you

On Feb 1, 3:21am, Stefano C. [email protected] wrote:

class Range
def each
current = @start
while (current <=> @end) < 1
yield current
current = current.succ
end
end
end

Your use of <=> is perplexing, given that anything Comparable would
have a nicer operator for this purpose.

On Tuesday 01 February 2011 17:43:44 Stefano G. wrote:

Thank you both, I feel ashamed that I didn’t think about “downto” …
Concerning my more general question, do you have any clue about why
decreasing ranges behave the way they do?

Thank you

Range#each calls the #succ method of the starting element to get the new
one
and goes on like this until the next element is greater than the last.
If
Range#each were written in ruby, I think it could be something like this
(it’s
just a guess, I didn’t look at the actual code):

class Range
def each
current = @start
while (current <=> @end) < 1
yield current
current = current.succ
end
end
end

In your case @start would be 4, while @end would be 1. Since 4 is
greater than
1, the block is never called.

Stefano

On Feb 1, 3:00pm, Stefano C. [email protected] wrote:

As you see, you don’t need to have a <, > or == method, just <=>.

Wacky.

On Wednesday 02 February 2011 03:30:15 Yossef M. wrote:

Your use of <=> is perplexing, given that anything Comparable would
have a nicer operator for this purpose.


-yossef

Yet, it’s what Range uses. According to “The ruby programming language”,
for
an object to be used as a Range endpoint, it needs to have a <=>. It
doesn’t
need to include Comparable. Look at this:

class X
def initialize n
@n = n
end
end

x1 = X.new(1)
x2 = X.new(5)

x1…x2
=> ArgumentError: bad value for range

class X
def <=> other
@n <=> other.instance_variable_get(:@n)
end
end

x1…x2
=> #<X:0x8286ad8 @n=1>…#<X:0x8284ea4 @n=5>

As you see, you don’t need to have a <, > or == method, just <=>.

Stefano

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs