我刚才有一个关于Ruby循环的快速问题。这两种迭代集合的方式有区别吗?
# way 1
@collection.each do |item|
# do whatever
end
# way 2
for item in @collection
# do whatever
end
只是想知道这些是否完全相同,或者是否有一个微妙的差异(可能是当@collection
为零)。
这是唯一的区别:
每:
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'
请参阅“The Evils of the For Loop”以获得一个很好的解释(考虑变量范围有一个小的差异)。
使用each
是considered more idiomatic使用Ruby。
你的第一个例子,
@collection.each do |item|
# do whatever
end
is more idiomatic。虽然Ruby支持像for
和while
这样的循环结构,但通常首选块语法。
另一个细微差别是你在for
循环中声明的任何变量都将在循环外部可用,而迭代器块中的那些变量实际上是私有的。
另一个不同..
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
看起来没什么区别,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
在循环结束后定义a
。 each
没有的地方。这是支持使用each
的另一个原因,因为临时变量的生命周期较短。
永远不要使用for
它可能会导致错误。
差异很微妙但可能会导致巨大的错误!
不要被愚弄,这不是关于惯用的代码或风格问题。这是在生产代码中避免几乎无法追踪的错误的问题。 Ruby的for
实现有一个严重的缺陷,不应该使用。始终使用each
循环,永远不要使用for
循环。
以下是for
介绍bug的示例,
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
。
据我所知,使用块而不是语言控制结构更加惯用。
我只是想对Ruby中的for循环做一个具体的观点。它看起来像是一个类似于其他语言的构造,但事实上它就像Ruby中的其他循环构造一样。实际上,for与for Enumerable对象一样,就像每个迭代器一样。
传递给for的集合可以是具有每个迭代器方法的任何对象。数组和散列定义了每个方法,许多其他Ruby对象也是如此。 for / in循环调用指定对象的每个方法。当迭代器产生值时,for循环将每个值(或每组值)分配给指定的变量(或变量),然后在body中执行代码。
这是一个愚蠢的例子,但是说明for for循环可以与具有每个方法的任何对象一起工作,就像每个迭代器的工作方式一样:
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循环中使用每个迭代器。我只想把开车带回家,以至于for循环没有任何神奇之处。它是一个表达式,它调用集合的每个方法,然后将其传递给它的代码块。因此,这是一个非常罕见的情况,您需要使用它。几乎总是使用每个迭代器(具有块范围的额外好处)。
(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'循环中,局部变量在每个循环后刷新。