为什么 RuboCop 建议将 .times.map 替换为 Array.new?
Why does RuboCop suggest replacing .times.map with Array.new?
RuboCop 建议:
Use Array.new
with a block instead of .times.map.
在 docs 中为警察:
This cop checks for .times.map calls. In most cases such calls can be replaced with an explicit array creation.
示例:
# bad
9.times.map do |i|
i.to_s
end
# good
Array.new(9) do |i|
i.to_s
end
我知道可以替换,但我感觉9.times.map
更接近英文语法,更容易理解代码的作用
为什么要更换?
后者性能更高;这是一个解释:Pull request where this cop was added
It checks for calls like this:
9.times.map { |i| f(i) }
9.times.collect(&foo)
and suggests using this instead:
Array.new(9) { |i| f(i) }
Array.new(9, &foo)
The new code has approximately the same size, but uses fewer method
calls, consumes less memory, works a tiny bit faster and in my opinion
is more readable.
I've seen many occurrences of times.{map,collect} in different
well-known projects: Rails, GitLab, Rubocop and several closed-source
apps.
Benchmarks:
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
I'm not sure now that Lint is the correct namespace for the cop. Let
me know if I should move it to Performance.
Also I didn't implement autocorrection because it can potentially
break existing code, e.g. if someone has Fixnum#times method redefined
to do something fancy. Applying autocorrection would break their code.
如果觉得可读性更好,就采纳吧
这是一个性能规则,您应用程序中的大多数代码路径可能对性能都不是关键的。就个人而言,我总是倾向于支持可读性而不是过早的优化。
也就是说
100.times.map { ... }
times
创建一个 Enumerator
对象
map
在无法优化的情况下枚举该对象,例如,预先不知道数组的大小,它可能必须动态地重新分配更多 space 并且它必须枚举通过调用 Enumerable#each
的值,因为 map
是以这种方式实现的
鉴于
Array.new(100) { ... }
new
分配大小为 N
的数组
- 然后使用本机循环填充值
当您需要映射调用固定次数的块的结果时,您可以选择:
Array.new(n) { ... }
和:
n.times.map { ... }
后者对于 n = 10
大约慢 60%,对于 n > 1_000
下降到大约 40%。
注:对数刻度!
RuboCop 建议:
Use
Array.new
with a block instead of.times.map.
在 docs 中为警察:
This cop checks for .times.map calls. In most cases such calls can be replaced with an explicit array creation.
示例:
# bad
9.times.map do |i|
i.to_s
end
# good
Array.new(9) do |i|
i.to_s
end
我知道可以替换,但我感觉9.times.map
更接近英文语法,更容易理解代码的作用
为什么要更换?
后者性能更高;这是一个解释:Pull request where this cop was added
It checks for calls like this:
9.times.map { |i| f(i) } 9.times.collect(&foo)
and suggests using this instead:
Array.new(9) { |i| f(i) } Array.new(9, &foo)
The new code has approximately the same size, but uses fewer method calls, consumes less memory, works a tiny bit faster and in my opinion is more readable.
I've seen many occurrences of times.{map,collect} in different well-known projects: Rails, GitLab, Rubocop and several closed-source apps.
Benchmarks:
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
I'm not sure now that Lint is the correct namespace for the cop. Let me know if I should move it to Performance.
Also I didn't implement autocorrection because it can potentially break existing code, e.g. if someone has Fixnum#times method redefined to do something fancy. Applying autocorrection would break their code.
如果觉得可读性更好,就采纳吧
这是一个性能规则,您应用程序中的大多数代码路径可能对性能都不是关键的。就个人而言,我总是倾向于支持可读性而不是过早的优化。
也就是说
100.times.map { ... }
times
创建一个Enumerator
对象map
在无法优化的情况下枚举该对象,例如,预先不知道数组的大小,它可能必须动态地重新分配更多 space 并且它必须枚举通过调用Enumerable#each
的值,因为map
是以这种方式实现的
鉴于
Array.new(100) { ... }
new
分配大小为N
的数组- 然后使用本机循环填充值
当您需要映射调用固定次数的块的结果时,您可以选择:
Array.new(n) { ... }
和:
n.times.map { ... }
后者对于 n = 10
大约慢 60%,对于 n > 1_000
下降到大约 40%。
注:对数刻度!