假设您有1,000个项目的列表并想要选择10.选择的条件很昂贵,但选择的机会很高(比如说99%)。
list.select do |item|
item if is_ok?(item)
end.take(10)
这段代码非常低效,因为它检查每个项目,当它需要很少检查超过10个时。
什么是更好,更有效和Rubyish的做法?
答案 0 :(得分:3)
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)
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的结果数组所必需的。