重复一段代码,直到块本身返回false?

时间:2017-01-19 15:32:50

标签: ruby loops

我想:

  1. 将块传递给方法调用,然后
  2. 将整个方法调用作为while循环的条件传递,
  3. 即使我不需要在循环中放置任何逻辑。
  4. 具体来说,我有一个数组,我想#reject!基于相当复杂的逻辑的某些元素。对#reject!的后续调用可能会删除之前传递中未删除的元素。当#reject!最终停止查找要拒绝的元素时,它将返回nil。此时,我希望循环停止并继续执行程序。

    我以为我可以做到以下几点:

    while array.reject! do |element|
        ...
      end
    end
    

    我还没有真正尝试过,但是这个结构会抛出vim的ruby语法高亮显示一个循环(它认为第一个dowhile语句,并认为第二个end实际上是封装方法的结束。我还尝试将其重写为附加到while块的内联begin...end修饰符,

    begin; end while array.reject! do |element|
      ...
    end
    

    但它仍然以同样的方式搞砸了突出显示。无论如何,感觉就像滥用while循环一样。

    我能想到的唯一方法就是将方法调用指定为proc:

    proc = Proc.new do
             array.reject! do |element|
               ...
             end
           end
    
    while proc.call do; end
    

    虽然有效,但感觉很笨拙,尤其是尾随do; end

    有没有优雅的方法来实现这个目标?

4 个答案:

答案 0 :(得分:5)

这不仅仅是vim,while array.reject! do |element|语法无效:

$ ruby -c -e 'while array.reject! do |element| end'
-e:1: syntax error, unexpected '|'
while array.reject! do |element| end
                        ^

您可以使用{ ... }代替do ... end

while array.reject! { |element|
    # ...
  }
end

loopbreak

loop do
  break unless array.reject! do |element|
    # ...
  end
end

更明确一点:

loop do
  r = array.reject! do |element|
    # ...
  end
  break unless r
end

答案 1 :(得分:5)

Ruby允许您将条件移动到循环语句的末尾。这样可以很容易地将结果存储在循环内部并根据条件进行检查:

begin
  any_rejected = arr.reject! { … }
end while any_rejected

这与执行end while arr.reject! { … }的工作方式相同,但在这里发生的事情要清楚得多,特别是对于复杂的reject!

答案 2 :(得分:2)

你是对的,Ruby解析器认为FEED_STORAGES = { '': 'path.to.CustomFileFeedStorage', 'file': 'path.to.CustomFileFeedStorage', } 属于do,并且不了解第二个while的来源。这是一个优先问题。

此代码只是为了表明可以完成。有关应该的完成方式,请参阅Stefan的answer

end

输出:

array = (1..1000).to_a

while (array.reject! do |element|
    rand < 0.5
  end)
  p array.size
end

答案 3 :(得分:1)

在我需要调用方法直到返回值是我想要的情况下,我的个人偏好是:

:keep_going while my_method

或者更简洁地说,我有时会使用:

:go while my_method

它是一行,您可以使用符号的内容来帮助记录正在进行的操作。有了你的阻止,我个人会创建一个proc / lambda并将其传递给拒绝以便清楚。

# Harder to follow, IMHO
:keep_going while array.reject! do |...|
  more_code
end

# Easier to follow, IMHO
simplify = ->(...){ ... }
:keep_simplifying while array.reject!(&simplify)