我知道shift,push和pop是用于添加/删除数组元素的Array方法,但我不确定内存中实际发生了什么。例如,方法pop删除数组的最后一个元素。它让我想起了堆栈中使用的LIFO命令,但我认为该元素并不像汇编编程那样“弹出”;而是整个数组的索引被移位。我真的不知道如果有人能帮助我,我会非常感激。
答案 0 :(得分:6)
Ruby适用于那些患有这种想法的程序员。我们相信那些实施的程序员,他们可以在优化性能和内存管理方面做得最好。
如果您只是好奇,这里是Rubinius中Array#shift的代码:
def shift(n=undefined)
Rubinius.check_frozen
if n.equal? undefined
return nil if @total == 0
obj = @tuple.at @start
@tuple.put @start, nil
@start += 1
@total -= 1
obj
else
n = Rubinius::Type.coerce_to(n, Fixnum, :to_int)
raise ArgumentError, "negative array size" if n < 0
slice!(0, n)
end
end
你可以看到,一个数组本身就是一个Rubinius :: Tuple,而在Tuple定义中,它是一个Rubinius :: Array。它的作用就是把开始位置放到下一个位置。我不确定他们会释放它所使用的空间(我认为它会这样),因为你必须深入挖掘。
在官方1.9.3中,我不知道它是如何实现的,因为它们在C中执行,并且它们很难阅读。如果你想了解更多细节,你可以在GitHub上分叉Rubinius,或者从ruby-lang.org派出官方1.9.3,并阅读源代码。您也可以了解有关C / C ++编程的更多信息:)
所以我很快就完成了官方1.9.3的代码,这是数组#shift函数定义:
static VALUE
rb_ary_shift_m(int argc, VALUE *argv, VALUE ary)
{
VALUE result;
long n;
if (argc == 0) {
return rb_ary_shift(ary);
}
rb_ary_modify_check(ary);
result = ary_take_first_or_last(argc, argv, ary, ARY_TAKE_FIRST);
n = RARRAY_LEN(result);
if (ARY_SHARED_P(ary)) {
if (ARY_SHARED_NUM(ARY_SHARED(ary)) == 1) {
rb_mem_clear(RARRAY_PTR(ary), n);
}
ARY_INCREASE_PTR(ary, n);
}
else {
MEMMOVE(RARRAY_PTR(ary), RARRAY_PTR(ary)+n, VALUE, RARRAY_LEN(ary)-n);
}
ARY_INCREASE_LEN(ary, -n);
return result;
}
这一行:
MEMMOVE(RARRAY_PTR(ary), RARRAY_PTR(ary)+n, VALUE, RARRAY_LEN(ary)-n);
它告诉我们它实际上将内存块偏移了n。这可能就是为什么官员跑得比Rubinius慢...... Rubinius利用大记忆,但节省时间;官方消耗更少的记忆但需要更多的时间......