Ruby 中的“for”与“each”

"for" vs "each" in Ruby

提问人:mportiz08 提问时间:7/21/2010 最后编辑:the Tin Manmportiz08 更新时间:11/17/2023 访问量:282715

问:

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

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

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

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

Ruby 循环 每次 迭代

评论


答:

35赞 Bayard Randel 7/21/2010 #1

你的第一个例子,

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

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

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

评论

0赞 max 8/5/2019
while并且实际上有一些非常具体的用途,无法用每个用途代替,例如生成唯一值或用于 REPL。until
0赞 Jonathan Sterling 7/21/2010 #2

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

2赞 BaroqueBobcat 7/21/2010 #3

看起来没有区别,在下面使用。foreach

$ 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

就像贝亚德说的,每个都更惯用。它对你隐藏了更多,不需要特殊的语言功能。根据 Telemachus 的评论

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

for a in [1,2]
  puts a
end

在循环完成后定义。哪里没有。这是赞成使用 的另一个原因,因为 temp 变量的寿命较短。aeacheach

评论

1赞 Telemachus 7/21/2010
可变范围方面存在细微差异(正如 yjerem、ChristopheD 和 Bayard 所提到的)。
0赞 akuhn 12/24/2016
不正确,不在下面使用。查看其他答案。foreach
0赞 Sagar Pandya 12/28/2016
@akuhn 如需进一步澄清,请参阅此问题及其两个出色的答案。
50赞 ChristopheD 7/21/2010 #4

请参阅“For 循环的弊端”以获得一个很好的解释(考虑到变量范围,有一个小差异)。

使用被认为是 Ruby 的惯用用法。each

评论

0赞 ChristopheD 4/9/2013
@zachlatta:感谢您的通知。我将编辑链接以指向文章的 webarchive.org 变体!
1赞 pnomolos 6/12/2014
graysoftinc.com/early-steps/the-evils-of-the-for-loop 是新的链接,现在 JEG2 的网站重新上线了。
337赞 Paige Ruten 7/21/2010 #5

这是唯一的区别:

每:

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

使用循环时,迭代器变量在块完成后仍然存在。对于循环,它不会,除非在循环开始之前已经将其定义为局部变量。foreach

除此之外,只是该方法的语法糖。foreach

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

异常:main:Object 的未定义局部变量或方法“@collection”

评论

4赞 cyc115 6/13/2018
是否有充分的理由让 X 留在 for 案例中,或者这种糟糕的设计是否:P?在我看来,与大多数其他语言相比,这是相当不直观的。
5赞 3limin4t0r 2/7/2019
@cyc115 保留在 for 方案中的原因是(一般来说)关键字不会创建新范围。ifunlessbeginforwhile 等都适用于当前范围。 但是接受块。块始终在当前范围之上添加自己的范围。这意味着无法从块外部访问在块中声明新变量(因此是新作用域),因为该附加作用域在那里不可用。x#each
0赞 Zhou Haibo 9/27/2022
所以这是否意味着是一个更好的选择,因为它在循环后从内存中释放迭代器。each
0赞 ShadowRanger 2/17/2023
@ChuckZHB:释放迭代器(假设没有其他引用)。只有它产生的最后一个价值可能会持续存在。这是好事还是坏事取决于你想要什么(有时在循环之后有最终值很有用)。
7赞 Mr. Black 8/2/2012 #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

7赞 akuhn 12/24/2016 #7

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

不要被愚弄,这与惯用的代码或风格问题无关。Ruby 的实现存在严重缺陷,不应使用。for

下面是一个引入 bug 的示例,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

为什么?

在循环中,变量被定义一次且只定义一次,然后将一个定义用于所有迭代。因此,每个块都引用相同的块,该块在循环结束时的值为错误!fornnquz

在循环中,每次迭代都会定义一个新变量,例如上面的变量被定义了三次。因此,每个块都引用具有正确值的单独块。eachnnn

0赞 Daniel Viglione 3/12/2019 #8

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

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

这是一个愚蠢的例子,但说明了 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 循环并没有什么神奇之处。它是一个表达式,它调用集合的每个方法,然后将其传递给其代码块。因此,这是一个非常罕见的情况,您需要将其用于 in。几乎总是使用每个迭代器(具有块范围的额外好处)。

1赞 Kelvin Tan 4/16/2019 #9
(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”循环中,局部变量在每个循环后仍然存在。在“each”循环中,局部变量在每个循环后刷新。