在Ruby中循环的首选方法是什么?

时间:2014-07-25 06:31:58

标签: ruby for-loop each

为什么each循环优先于Ruby中的for循环?时间复杂度是否存在差异,或者它们在语法上是否有所不同?

5 个答案:

答案 0 :(得分:7)

是的,这是两种不同的迭代方式,但希望这个计算有所帮助。

require 'benchmark'

a = Array( 1..100000000 )
sum = 0
Benchmark.realtime {
  a.each { |x| sum += x }
}

这需要5.866932秒

a = Array( 1..100000000 )
sum = 0
Benchmark.realtime {
  for x in a
    sum += x
  end
}

这需要6.146521秒。

虽然它不是进行基准测试的正确方法,但也存在一些其他限制。但是在一台机器上,每台机器似乎都要快一些。

答案 1 :(得分:4)

  • 在迭代中引用项的变量是临时的,并且在迭代之外没有意义。如果从迭代之外隐藏它会更好。对于外部迭代器,此类变量位于迭代块之外。在下文中,e仅在do ... end内有用,但与块分开,并在其外部写入;程序员看起来并不容易:

    for e in [:foo, :bar] do
      ...
    end
    

    使用内部迭代器,块变量在块内部定义,使用它。它更容易阅读:

    [:foo, :bar].each do |e|
      ...
    end
    
  • 此可见性问题不仅适用于程序员。关于范围意义上的可见性,外部迭代器的变量可以在迭代之外访问:

    for e in [:foo] do; end
    e # => :foo
    

    而在内部迭代器中,块变量在外部是不可见的:

    [:foo].each do |e|; end
    e # => undefined local variable or method `e'
    

    从封装的角度来看,后者更好。

  • 当你想嵌套循环时,变量的顺序会因外部迭代器而有些倒退:

    for a in [[:foo, :bar]] do
      for e in a do
        ...
      end
    end
    

    但是使用内部迭代器,顺序更直接:

    [[:foo, :bar]].each do |a|
      a.each do |e|
        ...
      end
    end
    
  • 使用外部迭代器,您只能使用硬编码的Ruby语法,并且还必须记住关键字与内部调用的方法之间的匹配(for调用each ),但对于内部迭代器,您可以定义自己的迭代器,从而提供灵活性。

答案 2 :(得分:3)

each是Ruby Way。实现具有脱钩益处的Iterator Pattern

另请检查:"for" vs "each" in Ruby

答案 3 :(得分:2)

一个有趣的问题。在Ruby中有几种循环方式。我已经注意到Ruby中有一个设计原则,当有多种方法可以做同样的事情时,它们之间通常会有微妙的差异,每个案例都有自己独特的用途,它解决了自己的问题。所以最后你最终需要能够写出(而不仅仅是阅读)所有这些内容。

关于for循环的问题,这类似于my earlier question whethe for loop is a trap

基本上有两种主要的显式循环方式,一种是通过迭代器(或者更常见的是块),例如

[1, 2, 3].each { |e| puts e * 10 }
[1, 2, 3].map { |e| e * 10 )
# etc., see Array and Enumerable documentation for more iterator methods.

连接到这种迭代方式的是类Enumerator,您应该努力理解。

另一种方式是按whileuntilfor循环进行Pascal-ish循环。

for y in [1, 2, 3]
  puts y
end

x = 0
while x < 3
  puts x; x += 1
end

# same for until loop

ifunless一样,whileuntil有尾部形式,例如

a = 'alligator'
a.chop! until a.chars.last == 'g'
#=> 'allig'

第三个非常重要的循环方式是隐式循环,或者通过递归循环。 Ruby非常具有可塑性,所有类都可以修改,可以为各种事件设置钩子,这可以被利用来产生最不寻常的循环方式。可能性是无穷无尽的,我甚至不知道从哪里开始谈论它们。也许一个好地方是blog by Yusuke Endoh,一个着名的艺术家,使用Ruby代码作为他的首选艺术材料。

为了证明我的意思,请考虑这个循环

class Object
  def method_missing sym
    s = sym.to_s
    if s.chars.last == 'g' then s else eval s.chop end
  end
end

alligator
#=> "allig"

答案 4 :(得分:1)

除可读性问题外,for循环在Ruby域中迭代,而each在本机代码中进行迭代,因此原则上each在迭代所有元素时应该更有效率阵列。

循环:

arr.each {|x| puts x}

循环for:

for i in 0..arr.length
   puts arr[i]
end

each情况下,我们只是将代码块传递给在机器的本机代码(快速代码)中实现的方法,而在for情况下,所有代码都必须被解释并运行考虑到Ruby语言的所有复杂性。

但是for更灵活,让您以比each更复杂的方式进行迭代,例如,使用给定步骤进行迭代。

修改

我没有遇到过你可以在调用each()之前使用step()方法跳过一个范围,所以我声称for循环的灵活性实际上是不合理的。