Array.reject !,它是如何工作的?

时间:2012-07-13 09:45:26

标签: ruby arrays object iteration

我今天制作了一个非常小的ruby脚本,它使用正则表达式来跟踪具有特定名称的文件中的某些内容,并在添加其替换之前删除该内容。 (否则在迭代期间会出现问题)。

我不太习惯ruby(自从我的假期工作开始于1-2周前才开始使用它),但我的一个习惯是在迭代时避免触摸列表(或大多数其他ADT使用索引)在他们身上(删除某些内容),与我正在使用的语言无关。

经过一番搜索后,我发现了一些可以提供帮助的Array函数。现在,我正在使用Array.reject!并且脚本的工作方式就像我希望它能够工作一样,但老实说我无法弄清楚为什么Array.reject! {|line| line =~ regex }在跳过数组中的对象时没有问题。这些来源ruby-docs& some random website,确认在迭代时更改会立即应用 ,这让我想知道它是如何不会搞砸的...被删除的行之间没有空格/单词,只有\n将下一个带到它自己的行当然(但这只是字符串结尾的一部分)。

有没有人对此有一个很好的解释?

2 个答案:

答案 0 :(得分:14)

Array#reject!使用for循环迭代数组的元素。这是C代码:

for (i = 0; i < RARRAY_LEN(ary); ) {
  VALUE v = RARRAY_PTR(ary)[i];
  if (RTEST(rb_yield(v))) {
    rb_ary_delete_at(ary, i);
    result = ary;
  } 
  else {
    i++;
  }
}

有趣的是,i语句中for没有递增。如果给reject!的块评估为true,则删除当前元素,ary[i]自动指向下一个元素。仅当评估为false时,i才会递增。

[a b c d].reject! {|x| x == b}

 0 <------- i # doesn't match => i++
[a b c d]

   1 <----- i # matches => delete ary[i]
[a b c d]

   1 <----- i # doesn't match => i++
[a c d]

     2 <--- i # doesn't match => finished
[a c d]

答案 1 :(得分:2)

以下是ary_reject_bang的源代码,reject!的C实现的核心。

static VALUE
ary_reject_bang(VALUE ary)
{
    long i;
    VALUE result = Qnil;

    rb_ary_modify_check(ary);
    for (i = 0; i < RARRAY_LEN(ary); ) {
        VALUE v = RARRAY_PTR(ary)[i];
        if (RTEST(rb_yield(v))) {
            rb_ary_delete_at(ary, i);
            result = ary;
        }
        else {
            i++;
        }
    }
    return result;
}

RARRAY_PTRruby.h中定义的宏,它允许您访问Ruby数组的底层C数组。实际删除是使用rb_ary_delete_at完成的,VALUE rb_ary_delete_at(VALUE ary, long pos) { long len = RARRAY_LEN(ary); VALUE del; if (pos >= len) return Qnil; if (pos < 0) { pos += len; if (pos < 0) return Qnil; } rb_ary_modify(ary); del = RARRAY_PTR(ary)[pos]; MEMMOVE(RARRAY_PTR(ary)+pos, RARRAY_PTR(ary)+pos+1, VALUE, RARRAY_LEN(ary)-pos-1); ARY_INCREASE_LEN(ary, -1); return del; } 使用其他一些宏来保持数组的顺序:

{{1}}