Ruby可枚举 - 找到最多n次匹配元素

时间:2015-09-30 03:02:06

标签: arrays ruby

我有以下数组:

arr = [1, 3, 2, 5, 2, 4, 2, 2, 4, 4, 2, 2, 4, 2, 1, 5]

我想要一个包含前三个奇数元素的数组。

我知道我可以这样做:

arr.select(&:odd?).take(3)

但我希望避免遍历整个数组,而是在我找到第三场比赛后返回。

我提出了以下解决方案,我认为这就是我想要的:

my_arr.each_with_object([]) do |el, memo| 
  memo << el if el.odd?; break memo if memo.size == 3 
end

但有没有更简单/惯用的方法呢?

2 个答案:

答案 0 :(得分:9)

lazy enumerator使用Enumerable#lazy

arr.lazy.select(&:odd?).take(3).force
# => [1, 3, 5]

force用于强制惰性枚举器进行求值。或者,您可以使用first,因为它非常渴望:

arr.lazy.select(&:odd?).first(3)
# => [1, 3, 5]

答案 1 :(得分:5)

代码和示例

arr.take_while.with_object([]) do |e,a|
  a << e if e.odd?
  a.size < 3
end
  #=> [1, 3, 5]

<强>基准

require 'fruity'

def compare_em(arr, m)
  compare(
    lazy:       -> { arr.lazy.select(&:odd?).take(m).force },
    take_while: -> { arr.take_while.with_object([]) { |e,a|
                       a << e if e.odd?; a.size < m } }
  )
end

n = 1e6
arr = (1..n).to_a.shuffle

获得前1000个元素:

compare_em(arr, 1e3)
  # Running each test 8 times. Test will take about 1 second.
  # take_while is faster than lazy by 2x ± 1.0

获取前10,000个奇数元素:

compare_em(arr, 1e4)
  # Running each test once. Test will take about 1 second.
  # take_while is faster than lazy by 2x ± 1.0

获得前100,000个元素:

compare_em(arr, 1e5)
  # Running each test once. Test will take about 3 seconds.
  # take_while is faster than lazy by 2x ± 0.1

我很惊讶lazy做得很好,因为它在基准测试中往往要慢得多。