This question already has an answer here:
-
Is there a reason that we cannot iterate on “reverse Range” in ruby?
11 answers
Why will (1..5).each
iterate over 1,2,3,4,5
, but (5..1)
will not? It returns the Range instead.
1.9.2p290 :007 > (1..5).each do |i| puts i end
1
2
3
4
5
=> 1..5
1.9.2p290 :008 > (5..1).each do |i| puts i end
=> 5..1
The easiest way to do that is use downto
5.downto(1) do |i| puts i end
Ranges use <=>
to determine if an iteration is over; 5 <=> 1 == 1
(greater-than), so it's done before it starts. Even if they didn't, ranges iterate using succ
; 5.succ
is 6
, still out of luck. A range's step
cannot be negative, so that won't work either.
It returns the range because each
returns what it was called on. Use downto
if it's the functionality itself you're looking for, otherwise the above answers your actual question regarding "why".
You can easily extend the Range class, in particular the each method, to make it compatible with both ascending and descending ranges:
class Range
def each
if self.first < self.last
self.to_s=~(/\.\.\./) ? last = self.last-1 : last = self.last
self.first.upto(last) { |i| yield i}
else
self.to_s=~(/\.\.\./) ? last = self.last+1 : last = self.last
self.first.downto(last) { |i| yield i }
end
end
end
Then, the following code will perform just as you'd expect:
(0..10).each { |i| puts i}
(0...10).each { |i| puts i}
(10..0).each { |i| puts i}
(10...0).each { |i| puts i}
This doesn't even really have anything to do with Ruby, it's just simple basic math: the range which starts with 5
and ends with 1
is empty. There is nothing to iterate over.
Because Ruby only does what it's told, not what you mean.
It can't tell whether you want to go in reverse (ie 5, 4, 3, 2, 1), or whether you really only want the numbers starting from 5 that are less than or equal to 1. It's theoretically possible that someone may want the latter, and because Ruby can't tell what you really want, it'll go with the latter.