我认为问题是不言自明的:如果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
的依赖关系。
答案 0 :(得分:2)
each
不是Enumerable
,它会返回Enumerator
(注意法术)is_a?(Enumerable)
。
为什么[1,2,3].with_index
不起作用?因为with_index
是Enumerator
的实例方法,而不是Enumerable
的方法。
答案 1 :(得分:2)
模块Enumerable
中的所有方法都是实例方法,要求其接收者是枚举器(类Enumerator的实例)。包含模块Enumerable
(使用Module#include)的所有类必须拥有一个实例方法each
,它返回一个枚举器。内置类的三个示例是Arrays#each,Hash#each和Range#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]]
。