【问题标题】:Ruby - Subclassing array to make it randomize when flattenedRuby - 子类化数组以使其在展平时随机化
【发布时间】:2010-03-24 11:09:27
【问题描述】:

我正在尝试在 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 时,flatten!从未在我的数组中调用。我认为 ruby​​ 只是调用其他方法来递归地展平数组,但我不知道那是什么。有什么建议吗?

【问题讨论】:

    标签: ruby arrays subclass random


    【解决方案1】:

    我不是这方面的绝对专家,但 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!”在修改后的数组上运行,以在某些情况下恢复性能。

    【讨论】:

      【解决方案2】:

      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 类并改用它并保持原来的扁平化!方法完好无损。

      【讨论】:

      • 这将随机化所有数组,而不仅仅是普通数组中的 RandArrays...
      • 没错。这可能是个问题,这就是为什么我会选择 flatten_with_randomize!方法。无需重新实现flatten!在 ruby​​ 中,并承受任何性能损失。
      猜你喜欢
      • 2011-09-27
      • 2020-06-14
      • 1970-01-01
      • 1970-01-01
      • 2019-12-26
      • 2012-08-12
      • 1970-01-01
      • 2017-01-14
      • 1970-01-01
      相关资源
      最近更新 更多