(1..999).to_a
这种方法是O(n)吗?我想知道转换是否涉及隐式迭代,因此Ruby可以将值逐个写入连续的内存地址。
答案 0 :(得分:3)
The method实际上比O(n)略差。它不仅进行了天真的迭代,而且还没有提前检查大小是多少,因此它必须在迭代时重复分配更多内存。 I've opened an issue for that aspect已经在邮件列表上讨论过几次(并简要添加到ruby-core)。问题是,像Ruby中的几乎任何东西一样,Range可以打开并搞乱,因此Ruby无法真正优化该方法。它甚至不能指望Range#size
返回正确的结果。更糟糕的是,有些枚举甚至将size
方法委托给to_a
。
通常,没有必要进行此转换,但如果您确实需要数组方法,则可以使用Array#fill代替,这样可以填充(可能预先分配的)数组使用从其指数中得出的值。
答案 1 :(得分:2)
Range.instance_methods(false).include? :to_a
# false
范围没有to_a
,它从Enumerable
混合继承它,所以它通过一次推送一个值来构建数组。这似乎是非常低效的,但我会让基准测试说明一切:
require 'benchmark'
size = 100_000_000
Benchmark.bmbm do |r|
r.report("range") { (0...size).to_a }
r.report("fill") { a = Array.new(size); a.fill { |i| i } }
r.report("array") { Array.new(size) { |i| i } }
end
# Rehearsal -----------------------------------------
# range 4.530000 0.180000 4.710000 ( 4.716628)
# fill 5.810000 0.150000 5.960000 ( 5.966710)
# array 7.630000 0.250000 7.880000 ( 7.879940)
# ------------------------------- total: 18.550000sec
#
# user system total real
# range 4.540000 0.120000 4.660000 ( 4.660249)
# fill 5.980000 0.110000 6.090000 ( 6.089962)
# array 7.880000 0.110000 7.990000 ( 7.985818)
这不奇怪吗?它实际上是最快的。只需使用构造函数,手动填充数组就会更快。
但正如Ruby的情况一样,不要过于担心。对于合理大小的范围,性能差异可以忽略不计。