2015-09-30 41 views
5

的n次出現我有以下的數組:紅寶石枚舉 - 找到最多匹配元件

arr = [1, 3, 2, 5, 2, 4, 2, 2, 4, 4, 2, 2, 4, 2, 1, 5] 

欲包含第一三個奇元素的數組。

我知道我可以做到這一點:

arr.select(&:odd?).take(3) 

,但我想避免在整個陣列的迭代,而是返回一旦我找到了第三場比賽。

我想出了以下解決方案,我相信做什麼,我想:

my_arr.each_with_object([]) do |el, memo| 
    memo << el if el.odd?; break memo if memo.size == 3 
end 

但就是有這樣做的更簡單的/慣用的方式?

回答

9

使用lazy enumeratorEnumerable#lazy

arr.lazy.select(&:odd?).take(3).force 
# => [1, 3, 5] 

force用於強制懶枚舉評估。或者,你可以使用first因爲它的渴望:

arr.lazy.select(&:odd?).first(3) 
# => [1, 3, 5] 
+0

我確認'force'有效,但是我找不到除[ruby lang]中的文檔外的文檔(http://docs.ruby-lang.org/ja/2.1.0/method/Enumerator=3a= 3aLazy/I/force.html)。你知道爲什麼它不在RDoc的文檔中嗎?是因爲它只是'to_a'的別名嗎? – sawa

+3

僅供參考 - 您可以使用'arr.lazy.select(&:odd?)。first(3)'並避免'force' - 'take'是懶惰的,而first'不是。 – Myst

+0

@sawa我也找不到它。如果你確實找到了,請隨時編輯我的答案。 –

5

碼,例如

arr.take_while.with_object([]) do |e,a| 
    a << e if e.odd? 
    a.size < 3 
end 
    #=> [1, 3, 5] 

基準

require 'fruity' 

def compare_em(arr, m) 
    compare(
    lazy:  -> { arr.lazy.select(&:odd?).take(m).force }, 
    take_while: -> { arr.take_while.with_object([]) { |e,a| 
         a << e if e.odd?; a.size < m } } 
) 
end 

n = 1e6 
arr = (1..n).to_a.shuffle 

獲得第一個1000個奇數元素:

compare_em(arr, 1e3) 
    # Running each test 8 times. Test will take about 1 second. 
    # take_while is faster than lazy by 2x ± 1.0 

獲取前10,000個奇數元素:

compare_em(arr, 1e4) 
    # Running each test once. Test will take about 1 second. 
    # take_while is faster than lazy by 2x ± 1.0 

獲得第10萬餘種元素:

compare_em(arr, 1e5) 
    # Running each test once. Test will take about 3 seconds. 
    # take_while is faster than lazy by 2x ± 0.1 

我很驚訝lazy做的相當不錯,因爲它往往是慢得多,比較,在基準測試中。