Ruby中的“ for”与“ each”


200

我只是对Ruby中的循环有一个简单的问题。这两种遍历集合的方式之间有区别吗?

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

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

只是想知道它们是否完全相同,或者是否存在细微的差异(可能@collection是nil时)。

Answers:


315

这是唯一的区别:

每:

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”


3
有充分的理由为什么x仍保留在for情况下,还是此不良设计:P?与大多数其他语言相比,这似乎很不直观。
cyc115

3
@ cyc115之所以x保留在for场景中,是因为(通常来说)关键字没有创建新的作用域。如果除非beginforwhile等都适用于当前范围。#each但是接受一个阻止。块始终在当前范围的顶部添加自己的范围。这意味着将无法从块外部访问在块中声明新变量(因此是新作用域),因为那里没有可用的附加作用域。
3limin4t0r


30

你的第一个例子

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

更惯用了。虽然Ruby支持像for和这样的循环结构while,但通常首选使用块语法。

另一个微妙的区别是,您在for循环内声明的任何变量都将在循环外可用,而在迭代器块内的变量实际上是私有的。


while并且until确实有一些非常具体的用途,无法相互替代,例如生成唯一值或用于REPL。
最大


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

就像Bayard所说的那样,每一个都是惯用的。它对您隐藏了更多,并且不需要特殊的语言功能。 Per Telemachus的评论

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

for a in [1,2]
  puts a
end

a循环完成后定义的叶子。哪里each没有。这是支持使用的另一个原因each,因为temp变量的生存期较短。


1
存在一个小的差异(如yjerem,ChristopheD和贝亚德提及)关于可变范围。
Telemachus

不正确,请for不要each在下面使用。查看其他答案。
akuhn '16

@akuhn有关进一步的说明,请参阅此问题及其出色的答案。
萨加尔·潘迪亚

2

永远不要使用for它可能会导致几乎无法追踪的错误。

不要上当,这与惯用的代码或样式问题无关。Ruby的实现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具有正确值的单独块。



0

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

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

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

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。几乎总是使用每个迭代器(具有块作用域的附加优点)。


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”循环中,局部变量在每个循环之后仍然存在。在“每个”循环中,局部变量在每个循环后刷新。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.