【问题标题】:"for" vs "each" in RubyRuby 中的“for”与“each”
【发布时间】: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 时)。

【问题讨论】:

    标签: ruby loops foreach iteration each


    【解决方案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。

    【讨论】:

    【解决方案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 %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

      【讨论】:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-05-21
              • 1970-01-01
              • 1970-01-01
              • 2015-11-12
              • 1970-01-01
              • 1970-01-01
              • 2012-08-06
              • 2011-07-21
              相关资源
              最近更新 更多