如果每个都是Enumerable而with_index是Enumerable,那么为什么每个迭代数组而不是with_index呢?

时间:2018-06-15 02:04:37

标签: ruby

我认为问题是不言自明的:如果each是一个Enumerable而with_index是一个Enumerable,那么为什么each可以迭代数组而不是with_index?< / p>

我将提供一个例子:

 alphabet =  [:a, :b, :c]
 => [:a, :b, :c] 
alphabet.each.class
 => Enumerator 
alphabet.each.with_index.class
 => Enumerator 
alphabet.each
 => #<Enumerator: [:a, :b, :c]:each> 
2.1.2 :036 > alphabet.with_index
NoMethodError: undefined method `with_index' for [:a, :b, :c]:Array

我认为两者都是Enumerables似乎不直观,但each可以响应数组但with_index不能。因此,当我尝试构建这些Enumerables时,我会忘记:

alphabet.each.with_index(1).reduce({}) do |acc, (letter,i)|
end

有时候我犯了这个错误:

alphabet.with_index(1).reduce({}) do |acc, (letter,i)|
end

忘记with_index必须链接到each的依赖关系。

2 个答案:

答案 0 :(得分:2)

each不是Enumerable,它会返回Enumerator(注意法术)is_a?(Enumerable)

为什么[1,2,3].with_index不起作用?因为with_indexEnumerator的实例方法,而不是Enumerable的方法。

答案 1 :(得分:2)

Enumerable模块

模块Enumerable中的所有方法都是实例方法,要求其接收者是枚举器(类Enumerator的实例)。包含模块Enumerable(使用Module#include)的所有类必须拥有一个实例方法each,它返回一个枚举器。内置类的三个示例是Arrays#eachHash#eachRange#each 1 因此,要将Enumerable包含在自定义类中,必须在该类上定义方法each(返回枚举器)。

当在包含Enumerable的类的实例上执行Enumerable中包含的方法时,Ruby会在实例和each方法之间插入方法Enumerable

例如,您可以将[1,2,3].map { |n| 2*n } #=> [2,4,6]视为[1,2,3].each.map { |n| 2*n }(使用Array#each),将{ :a = 1, :b => 2 }.map { |k,v| v } #=> [1,2]视为{ :a = 1, :b => 2 }.each.map { |k,v| v }(使用Hash#each)和(1..4}.map { |n| 2*n } #=> [2,4,6](1..4}.each.map { |n| 2*n }(使用Range#each)。

<强> with_index

现在让我们考虑方法with_index。哪些模块(当然包括类)具有实例方法with_index

ObjectSpace.each_object(Module).select { |m| m.instance_methods.include?(:with_index) }
  #=> [Enumerator::Lazy, Enumerator]

Enumerator::Lazy.superclass #=> Enumerator) 请参阅ObjectSpace#each_object

接下来,在除了这两个类之外的任何类上调用with_index将引发 no-method 异常。例如,这包括[1,2,3].with_index{ :a=>1 }.with_index。但是,具有返回枚举器的方法each的类可以通过(明确地)在类的实例和each之间插入with_index来使用方法Enumerator#with_index

例如 2

enum = [1,2,3].each.with_index 
  #=> #<Enumerator: #<Enumerator: [1, 2, 3]:each>:with_index>

然后也许就这样使用了:

enum.to_a
  #=> [[1, 0], [2, 1], [3, 2]]

这是因为

[1,2,3].each.class
  #=> Enumerator

因此,可以在枚举器Enumerator上调用类with_index的所有实例方法(包括[1,2,3].each)。

1完整列表由ObjectSpace.each_object(Class).select {|k| k.instance_methods.include?(:each)}给出 2人们通常会写enum = [1,2,3].each_with_index(参见Enumerable#each_with_index)而不是enum = [1,2,3].each.with_index,但后者有时用于利用with_index接受论证的事实(默认值为0),指定索引库。例如,[1,2,3].each.with_index(1).to_a #=> [[1, 1], [2, 2], [3, 3]]