【问题标题】:Elegant Loop Elsing in RubyRuby 中的优雅循环 Elsing
【发布时间】:2013-01-25 02:09:26
【问题描述】:

我必须编写一个 Ruby 方法:

  1. 遍历数组,如果其中一个元素符合特定条件,则执行 Foo。

  2. 如果没有一个数组元素符合条件,则做 Bar 的事情。

在任何其他语言中,我会在进入循环之前设置一个布尔变量,如果我执行 Foo.该变量的值会告诉我是否需要 Bar。但这感觉非常不雅。有人可以提出更好的方法吗?

编辑 一些非常好的答案,但由于我应该提到的一个细节,它们并不完全有效。 Foo 所做的事情是对匹配条件的数组元素执行的。另外,保证最多有一个元素符合条件。

【问题讨论】:

  • 你需要展示你为解决这个问题而写的东西。否则,您似乎在寻找答案。
  • @theTinMan 我什么都没写。在任何情况下,这都不是一大段代码。我已经足够了解编写有效的代码 我只是想学习像 Ruby 人一样思考。
  • 您应该接受@Alex Wayne 的回答。它解决了您问题的第二部分。
  • @louism Alex 的答案有其优点,但我有一个更好的答案。等我有时间会发布。

标签: ruby loops


【解决方案1】:

是否有任何项目匹配?如果是,那么做一些事情,不涉及匹配项。

if items.any? { |item| item.totally_awesome? }
  foo "we're totally awesome!"
else
  bar "not awesome :("
end

抓住第一个匹配的项目。如果存在,则对匹配的项目执行一些操作。

awesome_item = items.find { |item| item.totally_awesome? }
if awesome_item
  foo "#{awesome_item.name} is totally awesome!"
else
  bar "no items are awesome :("
end

获取所有匹配的项目。如果数组中有任何东西,那么对所有匹配的项目做一些事情。

awesome_items = items.find_all { |item| item.totally_awesome? }
if awesome_items.any?
  foo "#{awesome_items.size} totally awesome items!"
else
  bar "no items are awesome :("
end

【讨论】:

    【解决方案2】:

    你可以这样做:

    if array.any? { |elem| elem.condition }
      foo
    else
      bar
    end
    

    docEnumerable#any 执行以下操作:

    将集合的每个元素传递给给定的块。如果该块曾经返回 false 或 nil 以外的值,则该方法返回 true。

    【讨论】:

      【解决方案3】:

      你想要的是Enumerable#find

      例如:

      element = array.find { |x| x.passes_requirements? }
      element ? element.foo! : bar
      

      【讨论】:

      • 那很好。如果我没有找到一种方法,我会接受的。
      • 这绝对比标记的答案好得多,IMO,当然也更符合良好的 Ruby 代码的习惯。
      • “惯用语”?因为它使用 find 而不是 each?但这意味着它需要两个步骤来完成每个步骤都可以完成的事情。
      • @IsaacRabinovitch 它使用find 而不是each,是的。请注意,文档中没有提到 each 的返回值,但 find 的返回值非常清楚。这也更简洁易读。它甚至可以作为一个班轮完成(element = array.find { |x| x.passes_requirements? }) ? element.foo! : bar,它仍然会好得多。
      【解决方案4】:
      idx = the_array.index { |i| conditional(i) }
      if idx
        modify_object(the_array[idx])
      else
        no_matches
      end
      

      【讨论】:

        【解决方案5】:

        编辑:根据新的问题标准进行修改。

        found_index = nil
        my_array.each_with_index.detect { |elem, i| elem.condition? && found_index = i }
        if found_index.nil?
          do_not_found_case
        else
          my_array[found_index] = some_conversion(elem)
        end
        

        这不是很漂亮,但它完成了工作并且在第一场比赛中仍然短路。

        【讨论】:

        • 按照我所说的方式,这是对问题的一个很好的答案,但它不能解释我遗漏的一个事实:Foo 涉及更改与条件匹配的数组元素。
        • 更棘手。我想不出一个真正简短、优雅、Ruby 风格但仍然有效的方法来做到这一点。更详细但有效的后备方法是标记是否进行了更改。我会用一个解决方案进行编辑,但我很高兴看到一个更好的解决方案。
        • @SporkInventor 的下面更干净。
        【解决方案6】:

        感谢所有尝试回答问题的人。你们都没有提供我认为合适的答案,但你们都强迫我思考如何以 Ruby 方式做事(这是本练习的重点!)并帮助我想出了这个答案:

        我需要利用 Ruby 中的迭代器只是方法这一事实。所有方法都返回一个值,并且(奇怪的是)each 返回一个有用 值。如果迭代完成,它将返回您正在迭代的集合;如果您使用break 提前终止迭代,它会返回nil(或可选参数)。

        因此,在布尔上下文中,如果完成,则整个循环为 true,如果您中断,则为 false。因此

        bar if array.each do |element|
          if fooable(element) then
            foo(element)
            break
          end
        end
        

        【讨论】:

        • 这是 Ruby 的对立面,IMO。我不会把它记下来,我只是认为你应该把勾号移到别处。
        • 我不在乎你是否标记我。我确实在乎你不解释为什么要这样做。
        • 您还有其他几个答案,它们显示的代码更惯用、更清晰,以 预期 的方式使用库函数(例如,不依赖于 @987654327 的返回值@ 或 break 来自有其他可用标准功能的迭代),并且它们可以工作。然而,你决定给自己打勾,你还没有解释为什么你的答案更接近“Ruby 方式”,这显然是重点,或者为什么他们的“不合适”。
        • 记录了我使用的行为。我同意我的评价是主观的,但它比你的诱饵更具建设性。
        • 在你的问题中你有“unRubyishly inlegant”,而在你的答案中你有“Ruby 方式(这是本练习的重点!)”所以我认为我很清楚可以给出我对编写 Ruby 的或多或少的惯用方式的看法。没有悲剧,只是我的有效意见。
        猜你喜欢
        • 1970-01-01
        • 2010-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-24
        • 1970-01-01
        相关资源
        最近更新 更多