Why will a Range not work when descending? [duplic

2019-02-11 10:38发布

This question already has an answer here:

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

标签: ruby range
5条回答
地球回转人心会变
2楼-- · 2019-02-11 10:54

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

查看更多
Luminary・发光体
3楼-- · 2019-02-11 11:01

The easiest way to do that is use downto

5.downto(1) do |i| puts i end
查看更多
We Are One
4楼-- · 2019-02-11 11:08

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楼-- · 2019-02-11 11:08

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.

查看更多
劳资没心,怎么记你
6楼-- · 2019-02-11 11:12

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}
查看更多
登录 后发表回答