Arrays 如何从数组中获取第一个匹配项

Arrays 如何从数组中获取第一个匹配项,arrays,ruby,Arrays,Ruby,给定一个数组:[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]

我可以选择大于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将在几乎所有案例中返回第一个项目(并且数组已经排序)。此外,我们进行比较的代码相当繁重(例如,需要往返到db)

有没有ruby速记法可以先取第一个,然后停下来?类似于:

[1, 2, 3, 5, 8. 13].each do |num|
   return num if num > 3
end
只需使用:

您可以使用:

对于您的问题,这显然是最好的,但是假设集合很大,并且您希望第一个
n>1
元素大于
3
?使用
lazy
,您可以编写:

(0..100_000_000_000).lazy.select { |num| num > 3 }.first(2)
  # => [4, 5]

它会在眨眼之间执行。

使用二进制搜索,它会在
O(logn)

下面是一个细分:

最慢的:

  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

我希望你觉得这有用:)

谢谢。我在仔细阅读数组的API文档,但从未看过可枚举的。唉,对整个数组进行排序以找到大于
3
的第一个元素真是太疯狂了。假设数组很大,第一个
3
接近疯狂的开始?你不需要一个基准来断定排序是荒谬的。伊加德!有一个上升的投票
.find
O(n)
.sort
O(n log(n))
。奇怪的是,使用
排序
的解决方案比在足够大的
数组
上使用
查找
的解决方案要快。谢谢@Cary Swoveland,@Akavall的输入,我把这个颠倒过来了。。。然而,对于上面给定的场景,bsearch仍然是最重要的。因此,数组被排序了。我尝试了
DATA=array.new(1e6){rand 1e6}
的基准测试。结果是
find
最快,
bsearch
2.20倍慢,
lazy\u-select
12.66倍慢,
select
,126779倍慢,
sort\u-bsearch
,330906倍慢。这正是我所期望的。请注意,我没有建议对这个问题使用
lazy
。我的论点是,如果您想要获得多个满足块中条件的值,则可能需要使用
lazy
。我误解了
b搜索
,认为它也是先排序的。注意
sort\u bsearch
需要在脚本中修复(
DATA.sort\u bsearch…
)。@Akavail,您不能从只考虑包含11个元素的数组的基准中得出结论。所有方法都有设置时间,如果集合较小,则可能会淹没总数。由于集合较大,因此在需要优化时,此方法效果良好。但是我的问题需要优化,因为比较很难:更像是
[1,2,3,5,8,13]。选择{num{bcrypt(num)==bcrypt(3)}。首先
懒惰不会有多大帮助。正如我所说,使用
查找
解决问题,但是
懒惰
可能对变体有用。至于
select
是否应该与
lazy
一起使用,这主要取决于数组的大小
select
将对数组中的每个元素执行块计算(在执行
first
之前),而
lazy.select
将执行相同的计算,但在选择满足条件的第一个元素后将停止。我想你是在反对使用
select
lazy
。我不会不同意。
 $ 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