2015-09-07 47 views
2

给定一个数组提取第一个匹配项:[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 

回答

4

只需使用find

[1, 2, 3, 5, 8, 13].find { |num| num > 3 } 
#=> 5 
+0

感谢。我正在挖掘Array的API文档,但从来没有看过Enumerable。叹。 – berkes

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] 

其执行在一眨眼的功夫。

+0

这需要进行优化时才能正常工作**,因为该设置很大**。但我的问题需要优化**,因为比较很难**:更像'[1,2,3,5,8,13] .select {| num | bcrypt(num)== bcrypt(3)} .first'懒惰不会有太大的帮助。 – berkes

+0

正如我所说的,为你的问题使用'find',但'lazy'可能对变体有用。关于'select'是否应该和'lazy'一起使用,主要取决于数组的大小。 'select'将对数组中的每个元素执行块计算(在执行'first'之前),而'lazy.select'将执行相同的计算,但在选择满足条件的第一个元素之后将停止。我认为你是在反对使用'select','lazy'或不是。我不会不同意。 –

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 

我希望对您有所帮助:)

+1

对整个数组进行排序以查找大于“3”的第一个元素是疯狂的。假设阵列很大,第一个'3'接近疯狂的开始?你不需要一个基准来断定排序是荒谬的。哦天呐!有一个upvote! –

+0

'.find'是'O(n)','.sort'是'O(n log(n))'。奇怪的是,使用'sort'的解决方案比在一个足够大的'array'上使用'find'的解决方案更快。 – Akavall

+0

感谢@ Cary Swoveland,@ Akavall为您提供的信息,我得到了这个颠倒......但是,bsearch仍然是上述给定场景的所有人之王。因此,该数组是排序的。 – zee