2017-01-01 55 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。 –

相关问题