Почему RuboCop предлагает заменить файл с расширением Array.new?
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
Я знаю, что его можно заменить, но я чувствую, что 9.times.map
ближе к грамматике английского языка, и легче понять, что делает код.
Почему это должно быть заменено?
Ответы
Ответ 1
Последний более производительный; Вот объяснение: Запрос на извлечение, где был добавлен этот полицейский
Он проверяет такие звонки следующим образом:
9.times.map { |i| f(i) }
9.times.collect(&foo)
и предлагает использовать вместо этого:
Array.new(9) { |i| f(i) }
Array.new(9, &foo)
Новый код имеет примерно такой же размер, но использует меньше методов звонки, потребляет меньше памяти, работает чуть быстрее и на мой взгляд более читабелен.
Я видел много случаев раз. {Карта, собирать} в разных известные проекты: Rails, GitLab, Rubocop и несколько закрытых источников приложения.
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
Теперь я не уверен, что Линт - это правильное пространство имен для полицейского. Позволять я знаю, если я должен переместить его в Performance.
Также я не реализовал автокоррекцию, потому что она может потенциально сломать существующий код, например если кто-то переопределил метод Fixnum # times сделать что-то причудливое. Применение автокоррекции нарушит их код.
Ответ 2
Если вы чувствуете, что это более читабельно, идти с ним.
Это правило производительности, и большинство путей кода в вашем приложении, вероятно, не являются критичными для производительности. Лично я всегда открыт для предпочтения удобочитаемости по сравнению с преждевременной оптимизацией.
Что сказал
100.times.map { ... }
-
times
создает объект Enumerator
-
map
перечисляет этот объект без возможности оптимизации, например, размер массива не известен заранее, и может потребоваться динамическое перераспределение большего пространства, и он должен перечислять значения, вызывая Enumerable#each
поскольку map
реализована таким образом
В то время как
Array.new(100) { ... }
-
new
выделяет массив размером N
- А затем использует собственный цикл для заполнения значений
Ответ 3
Когда вам нужно отобразить результат блока, вызванного фиксированное количество раз, у вас есть выбор между:
Array.new(n) { ... }
и:
n.times.map { ... }
Последний вариант примерно на 60% медленнее для n = 10
, который снижается примерно до 40% для n > 1_000
.
Примечание: логарифмическая шкала!
![enter image description here]()