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
方法的语法糖。
当@collection
是nil
时,两个循环都会抛出异常:
例外:main:Object 的未定义局部变量或方法 `@collection'
【讨论】:
x 保留在 for 案例中是否有充分的理由,或者这是一个糟糕的设计:P?在我看来,与大多数其他语言相比,这相当不直观。 @cyc115x
保留在 for 场景中的原因是(一般而言)关键字不会创建新范围。 if、unless、begin、for、while 等均可使用当前范围。 #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 支持 for
和 while
等循环结构,但通常首选块语法。
另一个细微的区别是,您在 for
循环中声明的任何变量都将在循环外可用,而在迭代器块中的变量实际上是私有的。
【讨论】:
while
和 until
实际上有一些非常具体的用途,不能用它们代替,例如生成唯一值或用于 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
在循环结束后定义 a
。 each
没有。这是支持使用 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 循环中的循环,类似于“继续”[重复]
如何使 C++ 中的 for each 循环函数与自定义类一起使用