Ruby 中的“for”与“each”

Posted

技术标签:

【中文标题】Ruby 中的“for”与“each”【英文标题】:"for" vs "each" in Ruby 【发布时间】:2011-03-18 16:40:04 【问题描述】:

我刚刚有一个关于 Ruby 循环的简短问题。这两种遍历集合的方式有区别吗?

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

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

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

【问题讨论】:

【参考方案1】:

这是唯一的区别:

每个:

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'

【讨论】:

x 保留在 for 案例中是否有充分的理由,或者这是一个糟糕的设计:P?在我看来,与大多数其他语言相比,这相当不直观。 @cyc115 x 保留在 for 场景中的原因是(一般而言)关键字不会创建新范围。 ifunlessbeginforwhile 等均可使用当前范围。 #each 但是接受一个块。块总是在当前范围之上添加自己的范围。这意味着在块中声明一个新变量(因此是一个新范围)将无法从块外部访问,因为那里没有额外的范围。【参考方案2】:

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

使用each 就是considered more idiomatic 使用Ruby。

【讨论】:

@zachlatta:感谢您的通知。我将编辑链接以指向文章的 webarchive.org 变体! graysoftinc.com/early-steps/the-evils-of-the-for-loop 是新链接,现在 JEG2 的网站重新上线了。【参考方案3】:

你的第一个例子,

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

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

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

【讨论】:

whileuntil 实际上有一些非常具体的用途,不能用它们代替,例如生成唯一值或用于 REPL。【参考方案4】:

还有一个不同的..

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

【讨论】:

【参考方案5】:

永远不要使用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 %wfoo bar quz
  lib.method_with_block  n 
end

puts lib.method_that_uses_these_blocks

打印

quz
quz
quz

使用%wfoo bar quz.each |n| ... 打印

foo
bar
quz

为什么?

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

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

【讨论】:

【参考方案6】:

看起来没什么区别,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 所说,每个都更惯用。它对您隐藏更多,并且不需要特殊的语言功能。 根据 Telemachus 的评论

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

for a in [1,2]
  puts a
end

在循环结束后定义 aeach 没有。这是支持使用 each 的另一个原因,因为 temp 变量的生命周期较短。

【讨论】:

关于变量范围一个小区别(正如 yjerem、ChristopheD 和 Bayard 提到的)。 不正确,for 没有在下面使用each。查看其他答案。 @akuhn 如需进一步说明,请参阅此question 及其出色的答案。【参考方案7】:

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

【讨论】:

【参考方案8】:

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

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

这是一个愚蠢的例子,但说明了 for in 循环适用于任何具有 each 方法的对象,就像 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

现在是 each 迭代器:

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

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

【讨论】:

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

【讨论】:

以上是关于Ruby 中的“for”与“each”的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 中的“temps.each(&:valid?)”是啥意思? [复制]

在 Ruby 中,如何跳过 .each 循环中的循环,类似于“继续”[重复]

JAVA中的for-each循环与迭代

如何使 C++ 中的 for each 循环函数与自定义类一起使用

通过 for 循环,比较 Python 与 Ruby 编程思想的差别

Python之通过for循环比较Python与Ruby编程思想的差别