Why is sum so much faster than inject(:+)?
所以我在Ruby2.4.0中运行了一些基准测试,并意识到
1 | (1...1000000000000000000000000000000).sum |
立即计算,而
1 | (1...1000000000000000000000000000000).inject(:+) |
号
我花了这么长时间才放弃手术。我觉得
注意:
简短的回答
对于整数范围:
Enumerable#sum 返回(range.max-range.min+1)*(range.max+range.min)/2 。Enumerable#inject(:+) 迭代每个元素。
理论
1和
1 2 3 4 5 6 7 | if (RTEST(rb_range_values(obj, &beg, &end, &excl))) { if (!memo.block_given && !memo.float_value && (FIXNUM_P(beg) || RB_TYPE_P(beg, T_BIGNUM)) && (FIXNUM_P(end) || RB_TYPE_P(end, T_BIGNUM))) { return int_range_sum(beg, end, excl, memo.v); } } |
1 2 3 4 5 | VALUE a; a = rb_int_plus(rb_int_minus(end, beg), LONG2FIX(1)); a = rb_int_mul(a, rb_int_plus(end, beg)); a = rb_int_idiv(a, LONG2FIX(2)); return rb_int_plus(init, a); |
号
相当于:
1 | (range.max-range.min+1)*(range.max+range.min)/2 |
上述平等!
复杂性非常感谢@k_g和@hynek pichi vychodil为这部分!
总和埃多克斯1〔13〕需要三个加法、一个乘法、一个减法和一个除法。
它是一个常量,但乘法是O((log n)2),所以对于整数范围,
埃多克斯1〔15〕
需要99999999999999999999999999999 8个附加项!
加上是O(对数N),所以
以
很容易检查是否添加了Ruby整数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | module AdditionInspector def +(b) puts"Calculating #{self}+#{b}" super end end class Integer prepend AdditionInspector end puts (1..5).sum #=> 15 puts (1..5).inject(:+) # Calculating 1+2 # Calculating 3+3 # Calculating 6+4 # Calculating 10+5 #=> 15 |
。
实际上,来自
Enumerable#sum method may not respect method redefinition of"+"
methods such asInteger#+ .
号