【问题标题】:Is there any case to prefer for instead of each in Ruby?在 Ruby 中是否有任何情况可以代替每个情况?
【发布时间】:2013-12-10 11:33:33
【问题描述】:

我知道for i in arrarr.each 作用域略有不同,每个人都一直说迭代器是可取的,但我想知道是否有任何情况下循环是可取的,为什么是这样(因为迭代器更习惯用语)?

【问题讨论】:

  • 在 Ruby 中进行原型设计以加速 C 扩展时,我发现使用更多类似 C 的惯用语会有所帮助,需要转换的东西更少。但可能不是您正在寻找的答案。 . .
  • 此评论不作为答案,只是提供一些附加信息。根据这个this answerforeach 在后台都使用.each。主要区别(根据this answer from the same question)是迭代器变量的作用域。最终,除了 Neil Slater 的帖子之外,听起来可能没有任何技术上的理由比另一个更喜欢一个。

标签: ruby


【解决方案1】:

TL;DR

  • 在 Ruby 1.8 中使用 for 循环提高性能
  • 在现有项目中使用 for 循环到标准
  • 使用each 循环将副作用降至最低
  • 首选each循环。

each 最大限度地减少副作用

foreach 之间的主要区别在于范围。

each 函数需要一个块。块创建一个新的词法范围。这意味着在函数范围内声明的任何变量在函数之后将不再可用。

[1, 2, 3].each do |i|
  a = i
end

puts a
# => NameErrror: undefined local variable or method `a' for main:Object

鉴于:

for i in [1, 2, 3]
  a = i
end

puts a
# => 3

因此,使用each 语法可以最大限度地降低副作用的风险。

确定退出点?

也就是说,在某些特殊情况下,for 循环可能会有所帮助。具体来说,当找出循环退出的位置时。

for i in 1..3
  a = i

  break if i % 2 == 0
end

puts a
# => 0

虽然有更好的方法:

a = (1..3).each do |i|
  break i if i % 2 == 0
end

每个都更快(在 Ruby 2.0 中)

Benchmark.bm(8) do |x|
  x.report "For" do
    max.times do
      for i in 1..100
        1 + 1
      end
    end
  end

  x.report "Each" do
    max.times do
      (1..100).each do |t|
        1+1
      end
    end
  end
end

Ruby 2.0

               user     system      total        real
For        6.420000   0.000000   6.420000 (  6.419870)
Each       5.830000   0.000000   5.830000 (  5.829911)

Ruby 1.8.6(较慢的机器)

              user     system      total        real
For      17.360000   0.000000  17.360000 ( 17.409992)
Each     21.130000   0.000000  21.130000 ( 21.250754)

基准 2

如果您阅读评论线索,则在 foreach 中讨论了创建对象的速度。 link provided 具有以下基准(尽管我已经清理了格式并修复了语法错误)。

b = 1..10e5

Benchmark.bmbm (10) do |x|
  x.report "each {}" do
    b.each { |r| r + 1 }
  end

  x.report "each do end" do
    b.each do |r|
      r + 1
    end
  end

  x.report "for do end" do
    for r in b do
      r + 1
    end
  end
end

Ruby 2.0

                  user     system      total        real
each {}       0.150000   0.000000   0.150000 (  0.144643)
each do end   0.140000   0.000000   0.140000 (  0.143244)
for do end    0.150000   0.000000   0.150000 (  0.147112)

Ruby 1.8.6

                  user     system      total        real
each {}       0.840000   0.000000   0.840000 (  0.851634)
each do end   0.730000   0.000000   0.730000 (  0.732737)
for do end    0.650000   0.000000   0.650000 (  0.647186)

【讨论】:

  • 我明白这一点。我也知道内部 Ruby 转换为 each。但我想知道是否还有什么情况下会使用 for 而不是 each。
  • 我发现当性能成为问题时,for 循环可能是有益的。在 Ruby 中创建对象的成本很高,并且可能会在多次迭代的循环中导致性能下降。
  • @thomthom 您能否提供表明情况如此的基准?
  • @p0deje 如答案中所述,它仅在您不希望循环中定义的变量被限制在循环范围内时才有用,您几乎总是可以完全避免跨度>
  • @thomthom 我刚刚添加了基准。看起来 Ruby 1.8 对 for 循环的运行时间更快,但这已得到修复。
【解决方案2】:

我已经为 SketchUp 的 Ruby API 编写了一些插件,我发现在迭代大型集合(几何实体)时,我会在 each 块上使用 for in 循环获得更好的性能。

我认为这是因为 for in 循环没有创建它的本地范围,并且对象被重用,而不是像在 each 循环中那样为每次迭代创建。


编辑:速度增益取决于 Ruby 版本。使用本文使用的测试sn-phttp://blog.shingara.fr/each-vs-for.html

Ruby 1.8.6:

              user     system      total        real
For      14.742000   0.000000  14.742000 ( 14.777000)
Each     18.190000   0.000000  18.190000 ( 18.194000)

Ruby 2.0.0

               user     system      total        real
For        5.975000   0.000000   5.975000 (  5.990000)
Each       5.444000   0.000000   5.444000 (  5.438000)

自旧的 1.8.6 以来,情况有了很大改善。 (不过,SketchUp 扩展开发人员仍需要针对此版本进行优化。)

【讨论】:

  • 1.8.6 很久以前就脱离了支持状态。它也慢了很多,所以它用于比较是可疑的。也许 1.8.7 的最终版本会更合理,但 1.9.3 会更有意义。
  • 没错,如果你能控制 Ruby 平台的话。但是,如果您为 SketchUp 编写扩展,则必须处理 Ruby 1.8.6。
猜你喜欢
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 2019-05-21
  • 2013-11-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 2018-04-29
相关资源
最近更新 更多