2017-01-01 89 views
2

我正在處理一些惰性迭代,並希望能夠爲此迭代指定多個步驟。這意味着我想要在ab之間切換。所以,如果我有這個作爲有沒有一種方法可以在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..20).step(2)'是'1',這是我用於回答的慣例。 –

回答

3

你可以使用的cycleEnumerator組合:

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

Ruby沒有與多個值步進內置的方法。但是,如果您實際上不需要懶惰方法,則可以使用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的參數是Enumerable應該重複的次數,而不是範圍max 。你的例子從3迭代到121。 –

相關問題