2017-01-01 4 views
2

Я работаю с ленивой итерацией и хотел бы указать несколько шагов для этой итерации. Это означает, что я хочу, чтобы шаг чередовался между a и b. Так, если бы я имел это как диапазон (не ленивый только для упрощения)Есть ли способ указать многоэтапный шаг в Ruby?

(1..20).step(2, 4) 

Я хочу, чтобы мой полученный диапазон будет

1 # + 2 = 
3 # + 4 = 
7 # + 2 = 
9 # + 4 = 
13 # + 2 = 
15 # + 4 = 
19 # + 2 = 21 (out of range, STOP ITERATION) 

Однако, я не могу понять способ сделать это. Возможно ли это в Ruby?

+0

Какой должен быть первый элемент? '1' или' 3'? Это '1' для' (1..20) .step (2) ', так что это соглашение, которое я использовал для своего ответа. –

ответ

3

Вы можете использовать комбинацию cycle и Enumerator:

class Range 
    def multi_step(*steps) 
    a = min 
    Enumerator.new do |yielder| 
     steps.cycle do |step| 
     yielder << a 
     a += step 
     break if a > max 
     end 
    end 
    end 
end 

p (1..20).multi_step(2, 4).to_a 
#=> [1, 3, 7, 9, 13, 15, 19] 

Обратите внимание, что первый элемент является 1, так как первый элемент (1..20).step(2) также 1.

Он принимает во внимание exclude_end?:

p (1...19).multi_step(2, 4).to_a 
#=> [1, 3, 7, 9, 13, 15] 

И может быть ленивым:

p (0..2).multi_step(1,-1).first(20) 
#=> [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] 
p (0..Float::INFINITY).multi_step(*(1..100).to_a).lazy.map{|x| x*2}.first(20) 
#=> [0, 2, 6, 12, 20, 30, 42, 56, 72, 90, 110, 132, 156, 182, 210, 240, 272, 306, 342, 380] 

Вот вариант FizzBuzz, который генерирует все кратные 3 или 5, но не 15:

p (3..50).multi_step(2,1,3,1,2,6).to_a 
#=> [3, 5, 6, 9, 10, 12, 18, 20, 21, 24, 25, 27, 33, 35, 36, 39, 40, 42, 48, 50] 
+1

Отличный ответ! –

1

Рубин не имеет встроенный метод для активизации с несколькими значениями. Однако, если вам не нужен ленивый метод, вы можете использовать Enumerable#cycle с аккумулятором. Например:

range = 1..20 
accum = range.min 
[2, 4].cycle(range.max) { |step| accum += step; puts accum } 

В качестве альтернативы, вы можете построить свой собственный ленивый нумератор с Enumerator::Lazy. Это кажется излишним для данного примера, но может быть полезно, если у вас очень большой Range object.

+1

Синтаксис выглядит неплохо, но параметр для http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-cycle - это количество повторений, которое должен повторяться, а не диапазон max , Ваш пример повторяется от 3 до 121. –

Смежные вопросы