给定一个数组:[1, 2, 3, 5, 8. 13]
我可以选择大于3的所有项目:
[1, 2, 3, 5, 8. 13].select { |num| num > 3 }
(我知道简写select(&:>)
语法,这不是重点)
我现在可以轻松地返回第一个。
[1, 2, 3, 5, 8. 13].select { |num| num > 3 }.first
但是当实际比较变得越来越重要时,这并不是非常有效。我正在尝试优化我们拥有300多个项目的数组的情况,select几乎将在所有情况下返回第一个(并且数组已经排序)。此外,我们进行比较的代码非常繁重(例如,需要往返数据库)。
是否有红宝石的速记来获取第一个然后停止?类似于:
[1, 2, 3, 5, 8. 13].each do |num|
return num if num > 3
end
答案 0 :(得分:4)
只需使用find
:
arg.apache.avro % avro
答案 1 :(得分:3)
使用二进制搜索,它会在O(log n)
这是一个细分:
最慢的:
2.1.2 :011 > [1, 2, 3, 5, 8, 13].sort.bsearch { |num| num > 3 }
=> 5
慢:
2.1.2 :010 > [1, 2, 3, 5, 8, 13].find { |num| num > 3 }
=> 5
最快:
2.1.2 :012 > [1, 2, 3, 5, 8, 13].bsearch { |num| num > 3 }
=> 5
2.1.2 :013 >
这是我刚刚编写的用于比较所有方法的小脚本:
$ cat ./benchmark_find_bsrch.rb
#!/usr/bin/env ruby
require 'benchmark/ips'
DATA = Array(0..10)
def find
DATA.find { |num| num > 3 }
end
def sort_bsearch
[10,9,8,4,5,6,1,2,3,7].sort.bsearch { |num| num > 3 }
end
def bsearch
DATA.bsearch { |num| num > 3 }
end
def select
DATA.select { |num| num > 3 }
end
def lazy_select
DATA.lazy.select { |num| num > 3 }.first
end
Benchmark.ips do |bm|
bm.report('find'){ find }
bm.report('sort_bsearch'){ sort_bsearch }
bm.report('bsearch'){ bsearch }
bm.report('select'){select }
bm.report('lazy_select') {lazy_select}
bm.compare!
end
其中输出以下内容:
util-scripts$ ./benchmark_find_bsrch.rb
Calculating -------------------------------------
find 63.607k i/100ms
sort_bsearch 52.039k i/100ms
bsearch 95.260k i/100ms
select 57.218k i/100ms
lazy_select 11.850k i/100ms
-------------------------------------------------
find 1.130M (± 5.0%) i/s - 5.661M
sort_bsearch 809.723k (± 6.5%) i/s - 4.059M
bsearch 2.099M (± 6.1%) i/s - 10.479M
select 929.578k (± 2.6%) i/s - 4.692M
lazy_select 140.782k (± 8.1%) i/s - 711.000k
Comparison:
bsearch: 2098632.5 i/s
find: 1129912.5 i/s - 1.86x slower
select: 929578.3 i/s - 2.26x slower
sort_bsearch: 809722.5 i/s - 2.59x slower
lazy_select: 140782.2 i/s - 14.91x slower
我希望你觉得这很有用:)
答案 2 :(得分:2)
您可以使用Enumerator#lazy:
[1, 2, 3, 5, 8, 13].lazy.select { |num| num > 3 }.first
#=> 5
对于您的问题,Enumerable#find显然是最佳选择,但假设收集量很大,并且您希望第一个n > 1
元素大于3
?使用lazy
,您可以写:
(0..100_000_000_000).lazy.select { |num| num > 3 }.first(2)
# => [4, 5]
在眨眼之间执行。