Ruby中的“for”与“each”

irlmq6kh  于 2023-11-18  发布在  Ruby
关注(0)|答案(9)|浏览(138)

我有一个关于Ruby中循环的问题,这两种遍历集合的方式有什么区别吗?

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

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

字符串
只是想知道这些是否完全相同,或者是否有细微的区别(可能是当@collection为nil时)。

wj8zmpe1

wj8zmpe11#

这是唯一的区别:

each:

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”

xt0899hw

xt0899hw2#

参见“The Evils of the For Loop“以获得更好的解释(考虑到变量范围,有一个小的区别)。
使用each被认为是Ruby更惯用的用法。

f2uvfpb9

f2uvfpb93#

第一个例子

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

字符串
虽然Ruby支持像forwhile这样的循环结构,但块语法通常是首选。
另一个微妙的区别是,在for循环中声明的任何变量都可以在循环外使用,而迭代器块中的变量实际上是私有的。

v64noz0r

v64noz0r4#

永远不要使用for,它可能会导致几乎无法追踪的错误。
不要被愚弄,这不是关于惯用代码或风格问题,Ruby的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。Bug!
each循环中,每次迭代都会定义一个新的变量n,例如上面的变量n定义了三次。因此,每个块都引用一个具有正确值的单独的n

798qvoo8

798qvoo85#

还有一个区别:

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

sqserrrh

sqserrrh6#

看起来没有区别,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

字符串
就像贝亚德说的,每一种都更地道。它对你隐藏了更多,不需要特殊的语言特征。根据忒勒马科斯的评论
for .. in ..将迭代器设置在循环范围之外,因此

for a in [1,2]
  puts a
end


在循环结束后留下a。而each没有。这是支持使用each的另一个原因,因为临时变量的寿命更短。

mfpqipee

mfpqipee7#

(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'循环中,局部变量在每次循环后刷新。

mv1qrgav

mv1qrgav8#

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

hwamh0ep

hwamh0ep9#

我只想对Ruby中的for in循环提出一个特别的观点。它看起来像是一个类似于其他语言的结构,但实际上它是一个表达式,就像Ruby中的其他循环结构一样。事实上,for in就像each迭代器一样与ESTO对象一起工作。
传递给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方法,该方法将值返回到块。正如这里的每个人所说的,使用each迭代器肯定比使用for in循环更可取。我只是想让大家明白,for in循环没有什么神奇之处。它是一个表达式,调用集合的each方法,然后将其传递到其代码块。因此,这是一个非常罕见的情况下,你会需要使用的in。使用each迭代器几乎总是(与块作用域的额外好处)。

相关问题