我知道 shift、push 和 pop 是用于添加/删除数组元素的数组方法,但我不确定内存中实际发生了什么。例如,pop 方法删除数组的最后一个元素。它让我想起了堆栈中使用的 LIFO 顺序,但我认为该元素并没有像汇编编程中那样真正“弹出”;而不是整个数组的索引被移动。我真的不知道,所以如果有人可以帮助我,我将不胜感激。
问问题
1649 次
1 回答
7
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 上 fork Rubinius,或者从 ruby-lang.org 上 fork 官方 1.9.3,并阅读源码。您也可以了解有关 C/C++ 编程的更多信息 :)
于是我快速浏览了官方1.9.3的代码,这是array#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在大内存上占优势,但节省时间;官方消耗更少的内存,但需要更多的时间......
于 2012-04-21T03:36:48.787 回答