我正在尝试在 ruby 中对 Array 进行子类化,以使其在展平时随机化其元素!叫做。查看 Array#flatten ( http://ruby-doc.org/core/classes/Array.src/M002218.html ) 的源代码,看起来应该递归调用 flatten!在数组中包含的任何数组上。所以,我尝试做这样的事情:
class RandArray < Array
def randomize!
self.sort!{rand(3)-1}
end
def flatten!
randomize!
super
end
end
但是,当普通数组包含我的 RandArray 并且在普通数组上调用 flatten 时,请扁平化!我的数组中从未被调用过。我认为 ruby 只是调用其他一些方法来递归地展平数组,但我不知道那是什么。有什么建议吗?
最佳答案
我不是这方面的绝对专家,但 Ruby 的数组是用 C 代码编写的。这是展平的代码! :
static VALUE
rb_ary_flatten_bang(ary)
VALUE ary;
{
long i = 0;
int mod = 0;
VALUE memo = Qnil;
while (i<RARRAY(ary)->len) {
VALUE ary2 = RARRAY(ary)->ptr[i];
VALUE tmp;
tmp = rb_check_array_type(ary2);
if (!NIL_P(tmp)) {
if (NIL_P(memo)) {
memo = rb_ary_new();
}
i += flatten(ary, i, tmp, memo);
mod = 1;
}
i++;
}
if (mod == 0) return Qnil;
return ary;
}
正如您在这一行中看到的,
i += flatten(ary, i, tmp, memo);
这里是这个扁平化 C 函数的实现:
static long
flatten(ary, idx, ary2, memo)
VALUE ary;
long idx;
VALUE ary2, memo;
{
VALUE id;
long i = idx;
long n, lim = idx + RARRAY(ary2)->len;
id = rb_obj_id(ary2);
if (rb_ary_includes(memo, id)) {
rb_raise(rb_eArgError, "tried to flatten recursive array");
}
rb_ary_push(memo, id);
rb_ary_splice(ary, idx, 1, ary2);
while (i < lim) {
VALUE tmp;
tmp = rb_check_array_type(rb_ary_elt(ary, i));
if (!NIL_P(tmp)) {
n = flatten(ary, i, tmp, memo);
i += n; lim += n;
}
i++;
}
rb_ary_pop(memo);
return lim - idx - 1; /* returns number of increased items */
}
压平了!代码直接调用数组中任何元素的 C 平展函数来验证 rb_check_array_type 它不会返回到 ruby 代码。相反,它会直接绕过重载的实现来访问底层 C 结构。
不知道如何覆盖它,我认为一种方法可能是重新打开数组并重写展平和展平!功能与纯 ruby 相同。 您可能会受到性能影响,但随后您可以根据需要重载它。并且您始终可以使用别名来获得“flatten_native”和“flatten_native!”在修改后的数组上运行函数,以在某些情况下恢复性能。
关于Ruby - 子类化数组以使其在展平时随机化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2507106/