我今天制作了一个非常小的ruby脚本,它使用正则表达式来跟踪具有特定名称的文件中的某些内容,并在添加其替换之前删除该内容。 (否则在迭代期间会出现问题)。
我不太习惯ruby(自从我的假期工作开始于1-2周前才开始使用它),但我的一个习惯是在迭代时避免触摸列表(或大多数其他ADT使用索引)在他们身上(删除某些内容),与我正在使用的语言无关。
经过一番搜索后,我发现了一些可以提供帮助的Array
函数。现在,我正在使用Array.reject!
并且脚本的工作方式就像我希望它能够工作一样,但老实说我无法弄清楚为什么Array.reject! {|line| line =~ regex }
在跳过数组中的对象时没有问题。这些来源ruby-docs& some random website,确认在迭代时更改会立即应用 ,这让我想知道它是如何不会搞砸的...被删除的行之间没有空格/单词,只有\n
将下一个带到它自己的行当然(但这只是字符串结尾的一部分)。
有没有人对此有一个很好的解释?
答案 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_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;
}
使用其他一些宏来保持数组的顺序:
{{1}}