为什么下降时范围不起作用?

lar*_*hao 35 ruby range

为什么会(1..5).each迭代1,2,3,4,5,但(5..1)不会?它返回Range.

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
Run Code Online (Sandbox Code Playgroud)

Der*_*gan 49

最简单的方法是使用downto

5.downto(1) do |i| puts i end
Run Code Online (Sandbox Code Playgroud)

  • 在现代版本的Ruby中,你可以做`x = 5.downto(1)`并将`x`作为变量传递. (3认同)
  • [`Numeric#step`](http://ruby-doc.org/core/Numeric.html#method-i-step)更灵活一点; 它也需要一个步长,而[`Integer#downto`](http://ruby-doc.org/core/Integer.html#method-i-downto)只有一个限制. (2认同)

Dav*_*ton 22

范围用于<=>确定迭代是否结束; 5 <=> 1 == 1(大于),所以在它开始之前完成.即使他们没有,范围迭代使用succ; 5.succ是的6,还是运气不好.范围step不能是负数,因此也不起作用.

它返回范围,因为each返回它所调用的内容.使用downto如果它是你正在寻找它本身的功能,否则上述回答关于"为什么"的实际问题.


zml*_*zml 5

您可以轻松扩展Range类,尤其是each方法,使其与升序和降序范围兼容:

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
Run Code Online (Sandbox Code Playgroud)

然后,以下代码将按照您的预期执行:

(0..10).each  { |i| puts i}
(0...10).each { |i| puts i}
(10..0).each  { |i| puts i}
(10...0).each { |i| puts i}
Run Code Online (Sandbox Code Playgroud)

  • 很好的编码。采取这种方法时的一个风险是,您可能拥有现有的库,其中程序员假定向后范围没有迭代,而通过进行此更改,您会破坏该假设并将错误引入库的执行中。想象一下,左右边界由变量 x 和 y 表示,而不是常量 Fixnum。在这种情况下,程序员可能依赖于“正常”范围行为。这是在 Ruby 中修补类时的典型风险。 (2认同)