如何只选择一次运算符,而不是在循环中重复测试?

How to choose an operator just one time, instead of repeating the test inside a loop?

这是我要重构的内容:

items.each_with_index do |item, ndx|
  if side == :left
    condition = ndx < max
  else
    condition = ndx >= max
  end

  result_items << item if condition
end

我希望能够将 if/else/end 移到块外,因为 side 的值在块内永远不会改变。由于根据 side 的值唯一改变的是运算符,我希望能够做类似的事情:

if side == :left
  oper = <
else
  oper = >=
end

items.each_with_index do |item, ndx|
  result_items << item if ndx oper max
end

但是,当然,由于语法原因,这行不通。

有没有存储运算符的方法,或者有更好的方法来完成我在这里的任务?

查看 "proc" 和 "lambda"。它们使您能够持有对匿名函数的引用:

[4] pry(main)> func = proc {|left, right| left < right}
=> #<Proc:0x34c83c0@(pry):4>

[5] pry(main)> func.call(4,5)
=> true

[6] pry(main)> func.call(5,2)
=> false

请注意,'proc' 需要传递 "block",并且稍后将使用给定的参数 "call" 编辑该块。这与您使用 mapeach 的语法相同 - 这就是所有块。 proc 只需 returns 阻止而不是调用它。

因此,您的代码类似于:

if side == :left
  oper = proc{|a,b| a<b}
else
  oper = proc{|a,b| a>=b}
end

items.each_with_index do |item, ndx|
  result_items << item if oper.call(ndx, max)
end

我没有检查过那个代码,可能有一些错别字。

运算符只是方法:

ndx.public_send(:<=, max)

相同
ndx <= max

因此在循环外将 oper 设置为所需运算符的符号,并在循环内设置

result_items << item if ndx.public_send(oper, max)

(如果您仍然停留在 1.8,则必须使用 send 而不是 public_send

如果我理解你的意图,另一种方法是使用 takedrop:

result_items = if side == :left
    items.take max
else
    items.drop max
end

如果你可以稍微调整一下这个逻辑,你也可以达到同样的效果:

items.each_with_index do |item, ndx|
  # assuming it to be Fixnum since you are using index value
  result_items << item if (max - ndx).abs >= 0
end