Ruby #select, но выберите только определенное число - PullRequest
4 голосов
/ 28 марта 2012

Какой лучший способ в Ruby сделать что-то вроде my_array.select(n){ |elem| ... }, где n означает «Я хочу только вернуть n элементы и прекратить оценку после достижения этого числа»?

Ответы [ 6 ]

2 голосов
/ 28 марта 2012

Вы можете легко реализовать lazy_select:

module Enumerable
  def lazy_select
    Enumerator.new do |yielder|
      each do |e|
        yielder.yield(e) if yield(e)
      end
    end
  end
end

Тогда такие вещи, как

(1..10000000000).to_enum.lazy_select{|e| e % 3 == 0}.take(3)
# => [3, 6, 9] 

выполнить мгновенно.

2 голосов
/ 28 марта 2012

Это должно сработать:

my_array.select(n) { |elem| elem.meets_condition? }.take(n)

Однако, все равно будут оцениваться все предметы.

Если у вас есть ленивый перечислитель, вы можете сделать это более эффективным способом.

https://github.com/ruby/ruby/pull/100 показывает попытку включения этой функции.

1 голос
/ 28 марта 2012

Вы можете создать расширение типа Enumerable, которое имеет желаемую семантику selectn:

module SelectN
  def selectn(n)
    out = []
    each do |e|
      break if n <= 0
      if yield e
        out << e
        n -= 1
      end
    end
    out
  end
end

a = (0..9).to_a
a.select{ |e| e%3 == 0 } # [0, 3, 6, 9]
a.extend SelectN
a.selectn(1) { |e| e%3 == 0 } # [0]
a.selectn(3) { |e| e%3 == 0 } # [0, 3, 6]

# for convenience, you could inject this behavior into all Arrays
# the usual caveats about monkey-patching std library behavior applies
class Array; include SelectN; end
(0..9).to_a.selectn(2) { |e| e%3 == 0 } # [0,3]
(0..9).to_a.selectn(99) { |e| e%3 == 0 } # [0,3, 6, 9]
1 голос
/ 28 марта 2012

Похоже, что нельзя избежать традиционного цикла, если вы используете акции 1.8.7 или 1.9.2 ...

result = []
num_want = 4
i = 0
while (elem = my_array[i]) && my_array.length < num_want
  result << elem if elem.some_condition
  i += 1
end
0 голосов
/ 28 марта 2012

Почему бы не перевернуть это и не выполнить #take перед #select:

my_array.take(n).select { |elem| ... }

Это обеспечит выполнение вычислений только для n количества элементов.

РЕДАКТИРОВАТЬ:

Enumerable :: Lazy, как известно, медленнее, но если известно, что ваши вычисления требуют больших вычислительных затрат, чем медленная медлительность, вы можете использовать функцию Ruby 2.0:

my_array.lazy.select { |elem| ... }.take(n)

См .: http://blog.railsware.com/2012/03/13/ruby-2-0-enumerablelazy/

0 голосов
/ 28 марта 2012

Я думаю, что сломанный цикл можно сделать в старомодном стиле цикла с break или что-то вроде этого:

n = 5
[1,2,3,4,5,6,7].take_while { |e| n -= 1; n >= 0 && e < 7 }

В функциональном языке это будет рекурсия, но без TCO это не сделаетв Ruby много смысла.

ОБНОВЛЕНИЕ

take_while - глупая идея, как указал dbenhur, поэтому я не знаю ничего лучше, чем цикл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...