计算可枚举周期的迭代次数

时间:2014-07-15 17:17:06

标签: ruby enumerator

我想使用像[1,2,3].cycle这样的枚举器,并计算我完成迭代的次数。 [1,2,3].cycle.count创建一个无限循环,并且不会带来迭代计数。我正在玩纸牌游戏,它会循环播放玩家。游戏中很容易说:

@round = 0
if @turn == 1
  @round += 1
end

它有效。但我想知道如何更改count或仅为iter的{​​{1}}枚举器添加cycle

module Enumerable
  def cycle  
    super
    def count
      puts "Hi"
    end  
  end  
end  

由于Ruby中的所有东西都是一个Object,我应该能够在函数中创建函数,因为这种情况有效:

def x
  def y
    puts 1
  end
end
x.y
# => 1

如何仅在count枚举器中覆盖cycle的行为,或者至少在iter枚举器中创建工作方法cycle

5 个答案:

答案 0 :(得分:3)

你可以很容易地把这样的东西放在一起。像

这样的东西
class Iter < Array
  attr_reader :iteration

  def initialize(*args)
    super(*args)
    @pointer = 0
    @iteration = 1 # Current iteration
  end

  def next
    self[@pointer].tap {
      @pointer = (@pointer + 1) % size
      @iteration += 1 if @pointer == 0
    }
  end
end

iter = Iter.new [1,2,3]

7.times { puts 'iteration %d: %d' % [iter.iteration, iter.next] }

# iteration 1: 1
# iteration 1: 2
# iteration 1: 3
# iteration 2: 1
# iteration 2: 2
# iteration 2: 3
# iteration 3: 1    

答案 1 :(得分:3)

另一种选择,不需要计算下一次呼叫次数:

class CycledArray
  def initialize(arr)
    @cycle = arr.cycle.each_with_index
    @iteration_length = arr.length
  end

  def next
    @cycle.next.first
  end

  def iterations
    @cycle.peek.last/@iteration_length
  end
end

arr = CycledArray.new([1,2,3])
56.times { arr.next }
arr.next
# => 3
arr.iterations
# => 19

答案 2 :(得分:2)

这应该有效:

ary = [1,2,3]

ary.cycle.with_index do |n,i|
  iteration_number = i / ary.size
  puts "n: #{n} iteration: #{iteration_number}"
  break if i == 10
end

答案 3 :(得分:1)

这么多方法,呃?您也可以创建一个枚举器Array,创建一个枚举器arr.cycle并使用Enumerator#next逐步完成它:

class CycleArr < Array
  def initialize arr
    @sz = arr.size
    @enum = arr.cycle
  end

  def next
    @count = (@count ||= 0) + 1
    @enum.next
  end

  def cycle_count
    1 + (@count - 1) % @sz
  end  
end

c = CycleArr.new(['dog', 'cat', 'pig'])
7.times { p [c.next, c.cycle_count] }
  # ["dog", 1]
  # ["cat", 2]
  # ["pig", 3]
  # ["dog", 1]
  # ["cat", 2]
  # ["pig", 3]
  # ["dog", 1]

答案 4 :(得分:1)

上面的一些答案包含了返回iteration / cycle_count的方式中的隐藏问题;例如,如果在检查迭代之前请求数据,则iteration / count_cycle可能是错误的。一个更有用的方法是返回[object, iteration](类似于#each_with_index返回[object, index]的方式)并且会占用一个块。

根据我问here的类似问题,从@Ursus和@ 7stud绘图,我喜欢这个解决方案:

module Enumerable
  def each_with_iteration
    Enumerator.new do |y|
      iteration = 1
      enum = self.cycle
      loop do
        enum.peek # raises StopIteration if self.empty?
        self.size.times do
          e = [enum.next, iteration]
          y << (block_given? ? yield(e) : e)
        end
        iteration += 1
      end
    end
  end
end

这使它与Enumerable#each_with_index非常相似,例如:

>> enum = %w(dog duck sheep rabbit).each_with_iteration
=> #<Enumerator: ...>
>> 7.times { p enum.next }
["dog", 1]
["duck", 1]
["sheep", 1]
["rabbit", 1]
["dog", 2]
["duck", 2]
["sheep", 2]
=> 7
>> enum.first(7)
=> [["dog", 1],
 ["duck", 1],
 ["sheep", 1],
 ["rabbit", 1],
 ["dog", 2],
 ["duck", 2],
 ["sheep", 2]]

用一个块:

>> animals = %w(dog duck sheep rabbit)
=> ["dog", "duck", "sheep", "rabbit"]
>> enum = animals.each_with_iteration { |animal, iter| "This is #{animal} number #{iter}" }
=> #<Enumerator: ...>
>> enum.first(7)
=> ["This is dog number 1",
 "This is duck number 1",
 "This is sheep number 1",
 "This is rabbit number 1",
 "This is dog number 2",
 "This is duck number 2",
 "This is sheep number 2"]

这也可以在Hash,Set,Range或其他Enumerable上按预期工作。

编辑:

  1. 如果是self.empty,添加一个警卫以防止进入无限循环?

  2. 合并yield以便对某个区块进行操作。