Ruby:在内存中移动,推送和弹出

时间:2012-04-21 02:02:38

标签: ruby

我知道shift,push和pop是用于添加/删除数组元素的Array方法,但我不确定内存中实际发生了什么。例如,方法pop删除数组的最后一个元素。它让我想起了堆栈中使用的LIFO命令,但我认为该元素并不像汇编编程那样“弹出”;而是整个数组的索引被移位。我真的不知道如果有人能帮助我,我会非常感激。

1 个答案:

答案 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利用大记忆,但节省时间;官方消耗更少的记忆但需要更多的时间......