Ruby中的枚举器

时间:2012-11-12 19:30:07

标签: ruby enumerable

我在理解Ruby中的枚举器时遇到了麻烦。

请纠正我如果我错了,o.enum_for(:arg)方法应该将对象转换为枚举器,对象o上的每次迭代都应该调用arg方法? 令我困惑的是这行代码如何工作

[4, 1, 2, 0].enum_for(:count).each_with_index do |elem, index|
  elem == index
end

它应该计算有多少元素等于它们在数组中的位置,并且它可以工作。但是,我不明白实际发生了什么。 each_with_index每次迭代都调用count方法吗?如果有人能够解释,那就太好了。

2 个答案:

答案 0 :(得分:3)

来自编程Ruby:

  

计数 enum.count {| obj | block}→int

     

返回枚举中等于obj或者对象的对象的数量   block返回一个真值。

因此,如果块返回true,则枚举器访问每个元素并将1加到计数中,在这种情况下,这意味着元素是否等于数组索引。

我没有看到这种模式使用太多(如果有的话) - 我更倾向于:

[4, 1, 2, 0].each_with_index.select { |elem, index| elem == index }.count

修改

让我们看一下评论中的示例:

[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a, b|
  a + b
end

each_slice(2)一次获取数组2个元素,并为每个切片返回一个数组:

[4, 1, 2, 0].each_slice(2).map # => [[4,1], [2,0]]

在结果上调用map让我们对每个子数组进行操作,并将其传递给一个块:

[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a,b|
  puts "#{a.inspect} #{b.inspect}"
end

结果

4 1
2 0

ab凭借块参数“splatted”获取其值:

a, b = *[4, 1]
a # => 4
b # => 1

您也可以将数组切片作为参数:

[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| puts "#{a.inspect}"}

[4, 1]
[2, 0]

这可以让你这样做:

[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.inject(:+) } #=> [5,2]

或者如果您有ActiveSupport(即Rails应用程序),

[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.sum }

对我而言,这比原始示例更清晰。

答案 1 :(得分:0)

array.count通常可以占用一个块,但是它自己会返回一个fixnum,所以它不能像其他迭代器一样链接到.with_index(尝试array.map.with_index {|x,i ... }等等。)

.enum_for(:count)将其转换为枚举器,允许进行链接。它在数组成员上迭代一次,并保持其中有多少等于其索引的计数。所以count实际上只被调用一次,但只有在将数组转换为更灵活的东西之后才会被调用。