为什么RuboCop建议用Array.new替换.times.map?

shi*_*kia 35 ruby performance rubocop

RuboCop建议:

使用Array.new带而不是块.times.map.

在警察的文件中:

这个警察检查.times.map调用.在大多数情况下,可以使用显式数组创建来替换此类调用.

例子:

# bad
9.times.map do |i|
  i.to_s
end

# good
Array.new(9) do |i|
  i.to_s
end
Run Code Online (Sandbox Code Playgroud)

我知道它可以被替换,但我觉得9.times.map更接近英语语法,并且更容易理解代码的作用.

为什么要更换?

Mik*_*iet 34

最后一个是更高效,这是一个解释:拉请求添加此警察的地方

它检查这样的调用:

9.times.map { |i| f(i) }
9.times.collect(&foo)
Run Code Online (Sandbox Code Playgroud)

并建议使用此代替:

Array.new(9) { |i| f(i) }
Array.new(9, &foo)
Run Code Online (Sandbox Code Playgroud)

新代码具有大致相同的大小,但使用较少的方法调用,消耗较少的内存,工作速度更快,在我看来更具可读性.

我在不同的着名项目中看到了很多次.{map,collect}:Rails,GitLab,Rubocop和几个闭源应用程序.

基准:

Benchmark.ips do |x|
  x.report('times.map') { 5.times.map{} }
  x.report('Array.new') { Array.new(5){} }
  x.compare!
end
__END__
Calculating -------------------------------------
           times.map    21.188k i/100ms
           Array.new    30.449k i/100ms
-------------------------------------------------
           times.map    311.613k (± 3.5%) i/s -      1.568M
           Array.new    590.374k (± 1.2%) i/s -      2.954M

Comparison:
           Array.new:   590373.6 i/s
           times.map:   311612.8 i/s - 1.89x slower
Run Code Online (Sandbox Code Playgroud)

我现在不确定Lint是警察的正确命名空间.如果我将其移至Performance,请告诉我.

此外,我没有实现自动更正,因为它可能会破坏现有代码,例如,如果有人将Fixnum#times方法重新定义为做一些奇特的事情.应用自动更正会破坏他们的代码.

  • 在2年内,rubocop可能已被转移到另一个回购,并且链接可能已经死亡.我们都在这里教育自己:不理解一切,甚至不理解任何事情之间存在差异. (4认同)

aku*_*uhn 12

如果你觉得它更具可读性,那就去吧.

这是一个性能规则,应用程序中的大多数代码路径可能不是性能关键.就个人而言,我总是愿意接受过早优化的可读性.

那就是说

100.times.map { ... }
Run Code Online (Sandbox Code Playgroud)
  • times创建一个Enumerator对象
  • map枚举该对象而不能进行优化,例如,数据的大小不是预先知道的,它可能必须动态地重新分配更多的空间,并且必须通过调用来枚举值,Enumerable#each因为这map是通过这种方式实现的

Array.new(100) { ... }
Run Code Online (Sandbox Code Playgroud)
  • new 分配一个大小的数组 N
  • 然后使用本机循环来填充值

  • _ **“如果您觉得它更具可读性,那就去吧。” ** _谢谢!另外,如果您查看@Drenmi的答案,他将显示“时间”仅比“数组”慢60%。如果速度降低1000%,则可能会影响您的决定。但是,人类长期的可读性将弥补这些微小的性能差异。 (2认同)

Dre*_*nmi 5

当需要映射已调用固定次数的块的结果时,可以选择以下选项:

Array.new(n) { ... }
Run Code Online (Sandbox Code Playgroud)

和:

n.times.map { ... }
Run Code Online (Sandbox Code Playgroud)

后者的速度要慢60%n = 10,而速度则要慢40%n > 1_000

注意:对数刻度!

在此处输入图片说明