5

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

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

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

有人对此有很好的解释吗?

4

2 回答 2

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]自动指向下一个元素。只有当它评估为 时falsei才会递增。

[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]
于 2012-07-13T10:47:43.807 回答
2

这是 的源代码ary_reject_bang,是 的 C 实现的核心reject!

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_PTR是定义在 中的宏ruby.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;
}
于 2012-07-13T10:31:03.270 回答