Ruby中的“for”vs“each”

时间:2010-07-20 21:28:12

标签: ruby loops foreach iteration each

我刚才有一个关于Ruby循环的快速问题。这两种迭代集合的方式有区别吗?

# way 1
@collection.each do |item|
  # do whatever
end

# way 2
for item in @collection
  # do whatever
end

只是想知道这些是否完全相同或者是否存在细微差别(可能在@collection为零时)。

9 个答案:

答案 0 :(得分:299)

这是唯一的区别:

<强>每个

irb> [1,2,3].each { |x| }
  => [1, 2, 3]
irb> x
NameError: undefined local variable or method `x' for main:Object
    from (irb):2
    from :0

作为

irb> for x in [1,2,3]; end
  => [1, 2, 3]
irb> x
  => 3

使用for循环,迭代器变量在块完成后仍然存在。使用each循环时,除非在循环开始之前已将其定义为局部变量。

除此之外,for只是each方法的语法糖。

@collectionnil时,两个循环都会抛出异常:

  

异常:对于main:Object

,未定义的局部变量或方法`@collection'

答案 1 :(得分:43)

请参阅“The Evils of the For Loop”以获得一个很好的解释(考虑变量范围有一个小差异)。

使用eachconsidered more idiomatic使用Ruby。

答案 2 :(得分:27)

你的第一个例子,

@collection.each do |item|
  # do whatever
end

is more idiomatic。虽然Ruby支持像forwhile这样的循环结构,但通常首选块语法。

另一个细微差别是你在for循环中声明的任何变量都可以在循环外部使用,而迭代器块中的变量实际上是私有的。

答案 3 :(得分:6)

又一个......

number = ["one", "two", "three"]
 => ["one", "two", "three"] 

loop1 = []
loop2 = []

number.each do |c|
  loop1 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

for c in number
  loop2 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

loop1[1].call
two
 => nil 

loop2[1].call
three
 => nil 

来源:http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-ruby

更明确:http://www.ruby-forum.com/topic/179264#784884

答案 4 :(得分:2)

看起来没有区别,for下方使用each

$ irb
>> for x in nil
>> puts x
>> end
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):1
>> nil.each {|x| puts x}
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):4

就像贝亚德所说的那样,每一个都更具惯用性。它隐藏了更多内容,不需要特殊的语言功能。 Per Telemachus的评论

for .. in ..将迭代器设置在循环范围之外,所以

for a in [1,2]
  puts a
end
循环结束后定义

aeach没有的地方。这是支持使用each的另一个原因,因为临时变量的生命周期较短。

答案 5 :(得分:1)

永远不要使用for它可能会导致错误。

差异很小但可能会造成巨大的错误!

不要被愚弄,这不是关于惯用代码或样式问题。这是在生产代码中避免几乎无法追踪的错误的问题。 Ruby的for实现有一个严重的缺陷,不应该使用。始终使用each循环,永远不要使用for循环。

以下是for引入错误的示例

class Library
  def initialize
    @ary = []
  end
  def method_with_block(&block)
    @ary << block
  end
  def method_that_uses_these_blocks
    @ary.map(&:call)
  end
end

lib = Library.new

for n in %w{foo bar quz}
  lib.method_with_block { n }
end

puts lib.method_that_uses_these_blocks

打印

quz
quz
quz

使用%w{foo bar quz}.each { |n| ... }打印

foo
bar
quz

为什么?

for循环中,变量n仅定义一次,然后该定义用于所有迭代。因此,每个块引用相同的n,其在循环结束时具有值quz。错误!

each循环中,为每次迭代定义了一个新变量n,例如,在变量n之上定义了三次不同的时间。因此,每个块都引用具有正确值的单独n

答案 6 :(得分:0)

据我所知,使用块而不是语言控制结构更为惯用。

答案 7 :(得分:0)

我只想对Ruby中的for循环做一个具体说明。它看起来可能类似于其他语言的构造,但实际上,它是一个表达式,就像Ruby中的其他所有循环构造一样。实际上,for in可以与Enumerable对象一起使用,就像每个迭代器一样。

传递给for的集合可以是具有每个迭代器方法的任何对象。数组和哈希定义每个方法,许多其他Ruby对象也定义。 for / in循环调用指定对象的每个方法。当迭代器产生值时,for循环将每个值(或每个值集)分配给指定的一个或多个变量,然后在主体中执行代码。

这是一个愚蠢的示例,但说明了for in循环可与具有each方法的ANY对象一起工作,就像每个迭代器的工作方式一样:

class Apple
  TYPES = %w(red green yellow)
  def each
    yield TYPES.pop until TYPES.empty?
  end
end

a = Apple.new
for i in a do
  puts i
end
yellow
green
red
=> nil

现在是每个迭代器:

a = Apple.new
a.each do |i|
  puts i
end
yellow
green
red
=> nil

如您所见,两者都响应每种方法,这些方法将值返回给块。正如这里每个人都说过的,绝对最好在for in循环中使用每个迭代器。我只是想说说for循环没有什么神奇之处。它是一个表达式,它调用集合的每个方法,然后将其传递给其代码块。因此,这是一种非常罕见的情况,您需要用于in。几乎总是使用每个迭代器(具有块作用域的附加优点)。

答案 8 :(得分:0)

(1..4).each { |i| 


  a = 9 if i==3

  puts a 


}
#nil
#nil
#9
#nil

for i in 1..4

  a = 9 if i==3

  puts a

end
#nil
#nil
#9
#9

在“ for”循环中,局部变量在每个循环之后仍然有效。在“每个”循环中,局部变量在每个循环后刷新。