Decreasing range?

S

Stefano Grioni

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=C2=A0} , this will p=
rint
1234
However, if I write (4..1).each { |i| p i=C2=A0}, 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)=C2=A0} ?

Thanks a lot for your answer,

Best regards

-- =

Posted via http://www.ruby-forum.com/.=
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

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 }
 
R

Robert Klemme

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
 
S

Stefano Grioni

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
 
S

Stefano Crocco

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
 
Y

Yossef Mendelssohn

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

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

Stefano Crocco

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

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,998
Messages
2,570,242
Members
46,834
Latest member
vina0631

Latest Threads

Top