Count iteration on the Enumerable cycle

别说谁变了你拦得住时间么 提交于 2019-12-20 01:43:41

问题


I would like to use an enumerator like [1,2,3].cycle and count how many times I've gone through the iterations. [1,2,3].cycle.count creates an infinite loop and doesn't bring the iteration count. I'm playing a card game, and it cycles through the players. It's easy in the game to say:

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

and it works. But I would like to know how to change count or add iter only for enumerators with cycle into something like this:

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

Since everything in Ruby is an Object, I should be able to create functions within functions as this case works:

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

How can I overwrite the behaviour of count only within a cycle enumerator or at least create a working method iter within the cycle Enumerator?


回答1:


You can put something like that together fairly easily. Something like

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    



回答2:


Another option, which does not need to keep count of the number of calls to next:

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



回答3:


This should work:

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



回答4:


So many ways to do this, eh? You could also subclass Array, create an enumerator arr.cycle and step through it using 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]



回答5:


Some of the answers above contain hidden problems in the way iteration/cycle_count is returned; for example, the iteration/count_cycle may be wrong if the data is requested before the iteration is checked. A more useful method would return [object, iteration] (analogous to the way #each_with_index returns [object, index]) and would take a block.

Drawing from @Ursus and @7stud based on the similar question I asked here, I like this solution:

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

This makes it a close analog to Enumerable#each_with_index, for example:

>> 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]]

And with a block:

>> 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"]

This also works as expected on a Hash, Set, Range, or other Enumerable.

EDIT:

  1. Added a guard to keep from entering an infinite loop if self.empty?

  2. Incorporated yield to operate on a block.



来源:https://stackoverflow.com/questions/24764323/count-iteration-on-the-enumerable-cycle

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!