Array slicing in Ruby: explanation for illogical behaviour (taken from Rubykoans.com)

后端 未结 10 2237
没有蜡笔的小新
没有蜡笔的小新 2020-11-22 10:39

I was going through the exercises in Ruby Koans and I was struck by the following Ruby quirk that I found really unexplainable:

array = [:peanut, :butter, :a         


        
10条回答
  •  悲哀的现实
    2020-11-22 11:01

    this has to do with the fact that slice returns an array, relevant source documentation from Array#slice:

     *  call-seq:
     *     array[index]                -> obj      or nil
     *     array[start, length]        -> an_array or nil
     *     array[range]                -> an_array or nil
     *     array.slice(index)          -> obj      or nil
     *     array.slice(start, length)  -> an_array or nil
     *     array.slice(range)          -> an_array or nil
    

    which suggests to me that if you give the start that is out of bounds, it will return nil, thus in your example array[4,0] asks for the 4th element that exists, but asks to return an array of zero elements. While array[5,0] asks for an index out of bounds so it returns nil. This perhaps makes more sense if you remember that the slice method is returning a new array, not altering the original data structure.

    EDIT:

    After reviewing the comments I decided to edit this answer. Slice calls the following code snippet when the arg value is two:

    if (argc == 2) {
        if (SYMBOL_P(argv[0])) {
            rb_raise(rb_eTypeError, "Symbol as array index");
        }
        beg = NUM2LONG(argv[0]);
        len = NUM2LONG(argv[1]);
        if (beg < 0) {
            beg += RARRAY(ary)->len;
        }
        return rb_ary_subseq(ary, beg, len);
    }
    

    if you look in the array.c class where the rb_ary_subseq method is defined, you see that it is returning nil if the length is out of bounds, not the index:

    if (beg > RARRAY_LEN(ary)) return Qnil;
    

    In this case this is what is happening when 4 is passed in, it checks that there are 4 elements and thus does not trigger the nil return. It then goes on and returns an empty array if the second arg is set to zero. while if 5 is passed in, there are not 5 elements in the array, so it returns nil before the zero arg is evaluated. code here at line 944.

    I believe this to be a bug, or at least unpredictable and not the 'Principle of Least Surprise'. When I get a few minutes I will a least submit a failing test patch to ruby core.

提交回复
热议问题