シフト、プッシュ、ポップが配列の要素を追加/削除するために使用される配列メソッドであることは知っていますが、メモリ内で実際に何が起こっているのかわかりません。たとえば、配列の最後の要素を削除するメソッドpopを考えてみましょう。スタックで使用されているLIFOの順序を思い出しますが、要素はアセンブリプログラミングのように実際には「ポップ」されていないと思います。むしろ、配列全体のインデックスがシフトされます。本当にわからないので、誰か助けてくれたら本当にありがたいです。
1 に答える
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であり、タプルの定義では、Rubinius::Arrayです。それがすることはただ最初の位置を次の位置に置くことです。あなたがより深く掘り下げなければならないので、彼らがそれが使用したスペースを解放するかどうかはわかりません(私はそれがそうなると思います)。
公式1.9.3では、Cで行われているように、どのように実装されているのかわかりません。また、読みにくいです。詳細を知りたい場合は、GitHubでRubiniusをフォークするか、ruby-lang.orgから公式の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は大容量のメモリを利用しますが、時間を節約します。公式はより少ないメモリを消費しますが、より多くの時間がかかります...