Why will a Range not work when descending? [duplic

2019-02-11 10:35发布

问题:

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

回答1:

The easiest way to do that is use downto

5.downto(1) do |i| puts i end


回答2:

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



回答3:

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}


回答4:

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.



回答5:

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.



标签: ruby range