新手问题 array.flatten 怎么实现的

zsjacky · May 24, 2016 · Last by larrylv replied at May 24, 2016 · 2772 hits

static VALUE rb_ary_flatten(int argc, VALUE *argv, VALUE ary) { int mod = 0, level = -1; VALUE result, lv;

rb_scan_args(argc, argv, "01", &lv); if (!NIL_P(lv)) level = NUM2INT(lv); if (level == 0) return ary_make_shared_copy(ary);

result = flatten(ary, level, &mod); OBJ_INFECT(result, ary);

return result; }

这段是从ruby-doc上贴的源码,感觉像是截出来的一部分,不是完整的。array.flatten 方法对于很深的嵌套好像都有效,这段源码好像没用到嵌套。

使用 markdown 方便你我他

是完整的,重点是这行:result = flatten(ary, level, &mod);

flatten 方法的完整定义在这里 https://github.com/ruby/ruby/blob/eb15df1f894fa7235ccbcda7661f7d6a052045f6/array.c#L4508-L4564

static VALUE
flatten(VALUE ary, int level, int *modified)
{
    long i = 0;
    VALUE stack, result, tmp, elt;
    st_table *memo;
    st_data_t id;

    stack = ary_new(0, ARY_DEFAULT_SIZE);
    result = ary_new(0, RARRAY_LEN(ary));
    memo = st_init_numtable();
    st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue);
    *modified = 0;

    while (1) {
    while (i < RARRAY_LEN(ary)) {
        elt = RARRAY_AREF(ary, i++);
        if (level >= 0 && RARRAY_LEN(stack) / 2 >= level) {
        rb_ary_push(result, elt);
        continue;
        }
        tmp = rb_check_array_type(elt);
        if (RBASIC(result)->klass) {
        rb_raise(rb_eRuntimeError, "flatten reentered");
        }
        if (NIL_P(tmp)) {
        rb_ary_push(result, elt);
        }
        else {
        *modified = 1;
        id = (st_data_t)tmp;
        if (st_lookup(memo, id, 0)) {
            st_free_table(memo);
            rb_raise(rb_eArgError, "tried to flatten recursive array");
        }
        st_insert(memo, id, (st_data_t)Qtrue);
        rb_ary_push(stack, ary);
        rb_ary_push(stack, LONG2NUM(i));
        ary = tmp;
        i = 0;
        }
    }
    if (RARRAY_LEN(stack) == 0) {
        break;
    }
    id = (st_data_t)ary;
    st_delete(memo, &id, 0);
    tmp = rb_ary_pop(stack);
    i = NUM2LONG(tmp);
    ary = rb_ary_pop(stack);
    }

    st_free_table(memo);

    RBASIC_SET_CLASS(result, rb_class_of(ary));
    return result;
}
You need to Sign in before reply, if you don't have an account, please Sign up first.