【问题标题】:escaping the .each { } iteration early in Ruby在 Ruby 早期转义 .each { } 迭代
【发布时间】:2010-12-06 19:11:17
【问题描述】:

代码:

 c = 0  
 items.each { |i|  
   puts i.to_s    
   # if c > 9 escape the each iteration early - and do not repeat  
   c++  
 }  

我想获取前 10 个项目,然后离开“每个”循环。

我用什么替换注释行?有更好的方法吗?更符合 Ruby 的习惯?

【问题讨论】:

标签: ruby loops iterator enumerator


【解决方案1】:

虽然break 解决方案有效,但我认为更实用的方法确实适合这个问题。你想take 前 10 个元素并打印它们,所以试试

items.take(10).each { |i| puts i.to_s }

【讨论】:

  • 短:puts items.take(10)
  • 我刚刚尝试用谷歌搜索“ruby take 方法”,但找不到对 take 所在模块的引用。它在 API 中的什么位置?
  • 我想我需要升级我的 Ruby 版本:Nothing known about Array#take
  • 或者 ri Enumerable#take,我想。请注意,它在 Ruby 1.9 中似乎是新的。我在尝试 Ruby 1.8.6 时遇到无方法错误。
  • Array#take 存在于 Ruby 1.8.7 及更高版本中。
【解决方案2】:

Ruby 中没有++ 运算符。对多行块使用doend 也是惯例。修改您的解决方案会产生:

c = 0  
items.each do |i|  
  puts i.to_s    
  break if c > 9
  c += 1 
end

也可以:

items.each_with_index do |i, c|  
  puts i.to_s    
  break if c > 9
end

each_with_indexProgramming Ruby Break, Redo, and Next

更新: Chuck's answer with range 更像 Ruby,nimrodm's answer 使用 take 更好。

【讨论】:

  • 谢谢。回答和+1。哇,我对最初的语法很不满意。
  • 你离得不远了,真的:你的答案中唯一无效的部分是++。块的花括号可以工作,但多行块不是首选;见stackoverflow.com/questions/533008/…
  • 我喜欢你的第一个解决方案,因为如果你想每次循环超过 100 个项目但只有条件地取出 10 个,你可以独立增加计数器。
【解决方案3】:

break 用于提早从循环中转义,但仅使用items[0..9].each {|i| puts i} 更为惯用。 (如果您所做的只是打印完全没有更改的项目,您可以执行puts items[0..9]。)

【讨论】:

  • 我会写成:puts items[0..9].join("\n")
【解决方案4】:

另一种选择是

items.first(10).each do |i|
  puts i.to_s
end

对我来说,这比在迭代器上中断更容易一些,如果没有足够的项目,首先将只返回尽可能多的可用项目。

【讨论】:

    【解决方案5】:

    另一种变体:

    puts items.first(10)
    

    请注意,这适用于少于 10 个项目的数组:

    >> nums = (1..5).to_a
    => [1, 2, 3, 4, 5]
    >> puts nums.first(10)
    1
    2
    3
    4
    5
    

    (另外注意,很多人提供某种形式的puts i.to_s,但在这种情况下,.to_s 不是多余的吗?puts 会自动调用非字符串上的.to_s打印出来,我想。如果你想说puts 'A' + i.to_s或类似的话,你只需要.to_s。)

    【讨论】:

      【解决方案6】:

      这看起来像你想要的吗?

      10.times { |i|
        puts items[i].to_s
      }
      

      【讨论】:

      • 这可行,但我不能总是确定来源至少有 10 项。
      • 啊。您可以添加break if items[i] == nil,但此时each_with_index 看起来就像您应该使用的那样。
      【解决方案7】:
      items.each_with_index { |i, c| puts i and break if c <= 9 }
      

      【讨论】:

      • 这将在第一项之后中断。
      • 不是真的,你是怎么测试的?
      • @Khelll:这是因为对and 的懒惰评估吗?它有效,但对我来说有点太聪明了。我的大脑一直想要&gt;=,因为我看到“and break if”在一起。
      • 好的,我之前倒过,但还是错了:这 never 坏了。像这样看:if c &lt;= 9; puts i; break; endand break 永远不会被执行,因为puts i 总是 nil 并且一旦 c>9,if 语句的整个主体就不再被执行。如果您想证明永远不会到达该分支,请将 break 替换为 (puts "YOU WON'T SEE THIS")
      • @Chuck:谢谢你再来一轮。 @Khellll:我认为我们已经证明它读起来不太自然。
      【解决方案8】:

      有人问:

      我想获取前 10 个项目,然后离开“每个”循环。

      使用throwcatch 来完成此操作,只需对示例进行少量更改:

      catch(:done) do
          c = 0
          collected = []
          items.each do |item|
              collected << item
              throw(:done, collected) if c == 9 # started at 0
              c += 1
          end
          collected # if the list is less than 10 long, return what was collected
      end
      

      只需将throw 标签:donecollected 和正在等待:donecatch 返回collected

      然后把它“红宝石”一下:

      catch(:done) do
          items.inject([]) do |collected, item|
              throw(:done, collected) if collected.size == 10
              collected << item # collected gets returned here and populates the first argument of this block
          end
      end
      

      我不知道为什么有些人拒绝使用inject 而是使用reduce(它们是等效的),而显然给inject([]) 的空数组正在注入items!无论如何,如果少于 10 个项目,inject 将返回 collected

      大多数答案都试图回答问题的意图,而不是询问的内容,items.take(10) 在这种情况下确实很有意义。但我可以想象想要抢购符合我 100 美元预算的第一批物品。然后你可以简单地:

      catch(:done) do
          items.inject({items: [], budget: 100}) do |ledger, item|
              remainder = ledger[:budget] - item.price
              if remainder < 0
                  throw(:done, ledger)
              else
                  ledger.tap do |this|
                      this[:items] << item
                      this[:budget] = remainder
                  end # tap just returns what is being tapped into, in this case, ledger
              end
          end
      end
      

      【讨论】:

      • 你回答了一个简单的问题,却给出了一个非常复杂的答案。此处无需使用throwcatch 或将其变成13 行深度嵌套的代码。保持简单。
      • 我的答案是 6 行,它显示了另一种逃避被问到的每个循环的方法,它比大多数答案嵌套更深一层。我留下这个答案的希望是展示如何利用这个简单的上下文来退出循环。如果您真的阅读了我的帖子,我的 13 行代码是对我在回答中提出的更复杂示例的更复杂的回答。对于此回复中的字词过多,我提前道歉。
      猜你喜欢
      • 2019-03-29
      • 1970-01-01
      • 2010-12-14
      • 2012-10-17
      • 2013-09-02
      • 1970-01-01
      • 2015-05-21
      • 1970-01-01
      相关资源
      最近更新 更多