我不是这方面的绝对专家,但 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 */
}
压扁!代码直接为验证 rb_check_array_type 的数组的任何元素调用 C flatten 函数,它不会返回到 ruby 代码。相反,它会绕过您的重载实现直接访问底层 C 结构。
不确定如何覆盖它,我认为一种方法可能是重新打开 Array 并重写 flatten 和 flatten!用作纯红宝石。
你会受到性能的影响,但是你可以在你认为合适的时候超载它。而且您总是可以使用别名来获得“flatten_native”和“flatten_native!”在修改后的数组上运行,以在某些情况下恢复性能。