Ruby - 子类化数组以使其在展平时随机化

发布于 2024-08-26 04:57:14 字数 556 浏览 4 评论 0原文

我正在尝试在 ruby​​ 中对 Array 进行子类化,以使其在展平时随机化其元素!被称为。查看 Array#flatten 的源代码 (http://ruby- doc.org/core/classes/Array.src/M002218.html),看起来应该递归调用展平!在数组中包含的任何数组上。所以,我尝试做这样的事情:

class RandArray < Array
    def randomize!
        self.sort!{rand(3)-1}
    end
    def flatten!
        randomize!
        super
    end
end

但是,当普通数组包含我的 RandArray 并在普通数组上调用 flatten 时,请展平!我的数组中从未被调用过。我认为 ruby​​ 只是调用其他一些方法来递归地展平数组,但我不知道那是什么。有什么建议吗?

I'm trying to subclass Array in ruby to make it randomize its elements when flatten! is called. Looking at the source code for Array#flatten (http://ruby-doc.org/core/classes/Array.src/M002218.html), it looks like it should recursively call flatten! on any array contained within an array. So, I tried doing something like this:

class RandArray < Array
    def randomize!
        self.sort!{rand(3)-1}
    end
    def flatten!
        randomize!
        super
    end
end

However, when a normal array contains my RandArray and flatten is called on the normal array, flatten! is never called in my array. I figure ruby is just calling some other method to flatten the arrays recursively, but I can't figure out what that is. Any tips?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧人哭 2024-09-02 04:57:14

我不是这方面的绝对专家,但 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 平展函数,它不会返回到 ruby​​ 代码。相反,它会直接绕过重载实现来访问底层 C 结构。

不知道如何覆盖这个,我认为一种方法可能是重新打开数组并重写展平和展平!功能与纯红宝石相同。
您可能会受到性能影响,但随后您可以根据需要超载它。并且您始终可以使用别名来获得“flatten_native”和“flatten_native!”在修改后的数组上运行函数,以在某些情况下恢复性能。

I am not an absolute expert on this but Ruby's Array is written as C code. here is the code for flatten! :

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;
}

As you can see on this line,

i += flatten(ary, i, tmp, memo);

and here is the implementation for this flatten C function :

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 */
}

The flatten! code calls directly the C flatten function for any element of the array that validates rb_check_array_type it doesn't go back to the ruby code.Instead it accesses the underlying C structure directly bypassing your overloaded implementation.

Not sure how to override this, I think one way could be to reopen Array and rewrite the flatten and flatten! function as pure ruby.
You would take a performance hit, but then you would be able to overload it as you see fit. And you could always use aliasing to have a "flatten_native" and a "flatten_native!" function on your modified array, to get the perfs back on some cases.

鯉魚旗 2024-09-02 04:57:14

Jean 是正确的,flatten 在幕后调用 C 函数。您可以修补 Array 类并覆盖默认的展平!方法,同时仍然保留对原始方法的访问。

class Array
  alias_method :old_flatten!, :flatten!
  def flatten!
    self.old_flatten!
    self.sort!{rand(3)-1}
  end
end

或者你可以添加一个 flatten_with_randomize !到 Array 类并使用它来代替并保持原始扁平化!方法完好无损。

Jean is correct, flatten calls a C function behind the scenes. You could patch the Array class and override the default flatten! method, while still retaining access to the original method.

class Array
  alias_method :old_flatten!, :flatten!
  def flatten!
    self.old_flatten!
    self.sort!{rand(3)-1}
  end
end

Or you could just add a flatten_with_randomize! to the Array class and use that instead and keep the original flatten! method intact.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文