选择和接受Ruby的好方法是什么?

时间:2016-09-04 20:07:32

标签: ruby select iterator

假设您有1,000个项目的列表并想要选择10.选择的条件很昂贵,但选择的机会很高(比如说99%)。

list.select do |item|
  item if is_ok?(item)
end.take(10)

这段代码非常低效,因为它检查每个项目,当它需要很少检查超过10个时。

什么是更好,更有效和Rubyish的做法?

4 个答案:

答案 0 :(得分:3)

使用lazy enumerators

list2 = list.lazy.select { |item| is_ok?(item) }.take(10) # .to_a to get an array

根据@tadman评论,让我强调.select { |x| x if p(x) }是多余的,在过滤器中,您只使用谓词:select { |x| p(x) }

答案 1 :(得分:3)

这里的每个人都喜欢lazy,这是有充分理由的。唯一的缺点是lazy方法的声誉有点慢。如果您只想要通过测试的前10个项目,这不是问题,但如果n所需的项目数量很大,则可以考虑效率。在这种情况下,当n个项目通过测试时,简单地从方法返回一个数组可能会更好("短路")。

def select_so_many(arr, nbr_wanted)
  return [] if nbr_wanted.zero? 
  arr.each_with_object([]) do |item, a|
    next unless is_ok?(item)
    a << item
    return a if a.size == nbr_wanted
  end  
  nil
end

def is_ok?(n)
   n < 5
end

select_so_many([3,7,1,6,4], 2)
  #=> [3, 1]

答案 2 :(得分:2)

您可以使用lazy枚举器。一旦达到10个项目,它将停止迭代,因此它不会遍历数组中的每个项目。

list.lazy.select do |item|
  item if item == 2
end.first(10)

答案 3 :(得分:2)

使用lazy enumerators

irb(main):001:0> arr = 1.upto(1000)
=> #<Enumerator: 1:upto(1000)>
irb(main):003:0> arr.lazy.select { |n| print "#{n} "; n.even? }.first(5)
1 2 3 4 5 6 7 8 9 10
 => [2, 4, 6, 8, 10]

所以你可以看到,尽管在一个包含1000个项目的数组上调用了select,但实际上只调用了前10个迭代,因为这就是生成长度为5的结果数组所必需的。