【问题标题】:Why will a Range not work when descending? [duplicate]为什么 Range 在下降时不起作用? [复制]
【发布时间】:2012-02-08 00:21:44
【问题描述】:

为什么(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

【问题讨论】:

    标签: ruby range


    【解决方案1】:

    您可以轻松地扩展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
    

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

    (0..10).each  { |i| puts i}
    (0...10).each { |i| puts i}
    (10..0).each  { |i| puts i}
    (10...0).each { |i| puts i}
    

    【讨论】:

    • 编码很好。采用这种方法的一个风险是,您可能有现有的库,其中程序员假设向后范围没有迭代,并且通过进行此更改,您打破了该假设并将错误引入库的执行。想象一下左右边界由变量 x 和 y 表示,而不是常量 Fixnums。在这种情况下,程序员可能依赖于“正常”范围行为。这是在 Ruby 中对类进行猴子修补时的典型风险。
    • 这样的东西应该从一开始就在 ruby​​-code-base 中实现。
    【解决方案2】:

    因为 Ruby 只做它被告知的事情,而不是你 mean 的事情。

    它无法判断您是否要反向(即5、4、3、2、1),或者您是否真的只想要从5开始小于或等于1的数字。理论上是可能有人想要后者,因为 Ruby 无法说出你真正想要什么,所以它会选择后者。

    【讨论】:

    • 从5开始小于等于1的数字
    • 那不是很有建设性。
    • @Hamdan 你能解释一下为什么我的回答没有建设性吗?我不是在说任何人是白痴,我只是在解释 Ruby 不能按照 OP 所期望的方式工作。
    • 因为你其实没有解释原因。
    • @Hamdan 我还没有解释什么?
    【解决方案3】:

    这甚至与 Ruby 没有任何关系,只是简单的基本数学运算:以 5 开头并以 1 结尾的范围是空的。没有什么可以迭代的。

    【讨论】:

    • 是的,但是为什么 Ruby 不去“等一下,1 小于 5,让我们反过来吧”?
    • ruby 对用户非常友好,因此完全可以预期它会起作用。
    • @AndrewGrimm:因为 Ruby 无法分辨。 range 协议不要求右值响应&lt;=&gt;,只要求左值。
    • @gwho:我不认为为现有符号发明新的不兼容含义是非常用户友好的。每个对高中数学至少有肤浅知识的人都知道范围是如何工作的,并且会对此感到非常惊讶。请注意,这还需要对范围协议进行向后不兼容的更改,因为目前,迭代范围所需的唯一事情是左值响应 succ&lt;=&gt;,这还不够向下迭代。
    【解决方案4】:

    范围使用&lt;=&gt; 来确定迭代是否结束; 5 &lt;=&gt; 1 == 1(大于),所以它在开始之前就完成了。即使他们没有,范围使用succ 进行迭代; 5.succ6,仍然不走运。范围的step 不能为负数,因此也不起作用。

    它返回范围,因为each 返回它被调用的内容。如果您正在寻找功能本身,请使用downto,否则以上内容回答了您关于“为什么”的实际问题。

    【讨论】:

    • 很好的解释,现在我都明白了。
    【解决方案5】:

    最简单的方法是使用 downto

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

    【讨论】:

    • 非常好,谢谢德里克!
    • 在现代版本的 Ruby 中,您可以使用 x = 5.downto(1) 并将 x 作为变量传递。
    • 不错。范围自动将块作为每个迭代器处理。无需显式调用.each
    • Numeric#step 更灵活一点;它也需要一个步长,而Integer#downto 只需要一个限制。
    • 您也可以将它与日期一起使用。太酷了
    猜你喜欢
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 2020-08-20
    • 2018-08-28
    • 2016-01-24
    • 2013-03-05
    • 2012-10-01
    • 2014-07-07
    相关资源
    最近更新 更多