【问题标题】:Array.reject!, how does it work exactly?Array.reject!,它是如何工作的?
【发布时间】:2012-07-13 03:46:53
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: ruby arrays object iteration


    【解决方案1】:

    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++;
      }
    }
    

    有趣的部分是ifor 语句中没有递增。如果分配给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]
    

    【讨论】:

    • 我想我回复了错误的评论,您可能不会收到通知,因为您实际上在他的帖子之前给出了答案,只是这样说是为了通知您我已经阅读/从您的评论中学习; p.
    • 看这篇优秀的文章:matthewcarriere.com/2008/06/23/…
    【解决方案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;
    }
    

    【讨论】:

    • @Stephan,好吧,我没有看到它来了……但它完全有道理,并且确实是安全删除对象的非常有用的实现。非常感谢您的解释!如果我有 15 个代表,我会投票:p - 已解决
    猜你喜欢
    • 1970-01-01
    • 2021-09-26
    • 2011-03-18
    • 2015-06-16
    • 2012-08-31
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    相关资源
    最近更新 更多