如何计算迭代器调用yield的次数?

时间:2014-02-28 17:07:38

标签: ruby iterator yield

我有一个产生对象的方法foo。我想计算它产生的对象数量。

我有

def total_foo
  count = 0
  foo { |f| count += 1}
  count
end

但可能有更好的方法。对这个新Rubyist的任何想法?

这是foo的定义(它是Rails中的辅助方法):

def foo(resource=@resource)
  resource.thingies.each do |thingy|
    bar(thingy) { |b| yield b }  # bar also yields objects
  end
end

2 个答案:

答案 0 :(得分:3)

任何调用yield的方法都可用于构建Enumerator对象,您可以通过count方法调用Object#to_enum。请记住,当您调用count时,迭代器实际上已执行,因此它应该没有副作用!遵循模仿您的场景的可运行示例:

@resources = [[1,2], [3,4]]

def foo(resources = @resources)
  resources.each do |thingy|
    thingy.each { |b| yield b }
  end
end

foo { |i| puts i }
# Output:
# 1
# 2
# 3
# 4

to_enum(:foo).count
# => 4

您可以将参数传递给foo

to_enum(:foo, [[5,6]]).count
# => 2

或者,您可以定义foo以在没有块的情况下调用Enumerator,这是stdlib迭代器的工作方式:

def foo(resources = @resources)

  return to_enum(__method__, resources) unless block_given?

  resources.each do |thingy|
    thingy.each { |b| yield b }
  end
end

foo.count
# => 4

foo([[1,2]]).count
# => 2

foo([[1,2]]) { |i| puts i }
# Output:
# 1
# 2

当您在to_enum上致电size以返回值时,您可以将阻止传递给Enumerator

def foo(resources = @resources)
  unless block_given?
    return to_enum(__method__, resources) do
      resources.map(&:size).reduce(:+) # thanks to @Ajedi32
    end
  end

  resources.each do |thingy|
    thingy.each { |b| yield b }
  end
end

foo.size
# => 4

foo([]).size
# => 0

在这种情况下,使用sizecount快一点,您的里程可能会有所不同。

答案 1 :(得分:1)

假设您只关心foo的副作用,您可以让foo自己计算迭代次数:

def foo(resource=@resource)
  count = 0
  resource.thingies.each do |thingy|
    bar(thingy) do |b|
      count += 1
      yield b
    end  # bar also yields objects
  end
  count
end

然后:

count = foo { |f| whatever... }

如果您选择,您也可以忽略返回值,所以只需:

foo { |f| whatever... }

如果你不在乎计数是什么。

根据更大的背景,可能有更好的方法来处理所有这些。