Ruby Enumeration: взято первым n, где блок возвращает true

Я хочу взять первые «n» записей, которые проходят блок

a = 1..100_000_000 # Basically a long array

# This iterates over the whole array -- no good
b = a.select{|x| x.expensive_operation?}.take(n)

Я хочу закоротить итерацию, как только у меня будет n записей, где «дорогое» условие истинно.

Что ты посоветуешь? take_while и вести подсчет n?

# This is the code i have; which i think can be written better, but how?
a = 1..100_000_000 # Basically a long array
n = 20
i = 0
b = a.take_while do |x|
  ((i < n) && (x.expensive_operation?)).tap do |r|
    i += 1
  end
end

person Aditya Sanghi    schedule 28.10.2012    source источник
comment
Мне кажется, что ваше решение выбирает некоторое значение x, даже если x.expensive_operation? ложно... это то, что вы хотите?   -  person Baldrick    schedule 28.10.2012
comment
хм... вы правы в том, что мое решение не кажется правильным, но не так, как вы предлагаете. оно остановится на первом значении, где дорогая_операция имеет значение false, возвращая мне меньше, чем n значений.   -  person Aditya Sanghi    schedule 28.10.2012


Ответы (2)


Ruby 2.0 реализует ленивые перечисления, для более старых версий используйте гем enumerable-lazy:

require 'enumerable/lazy'
(1..Float::INFINITY).lazy.select(&:even?).take(5).to_a
#=> [2, 4, 6, 8, 10]
person tokland    schedule 28.10.2012

Он должен работать с простым циклом for и break :

a = 1..100_000_000 # Basically a long array
n = 20
selected = []
for x in a
  selected << x if x.expensive_operation?
  break if select.length == n
end
person Baldrick    schedule 28.10.2012