tags:

views:

133

answers:

1

If I have an array a:

  1. a[a.length] returns nil. Good.
  2. a[a.length, x] returns []. Good.
  3. a[a.length+x, y] returns nil. Inconsistent with 2.

While this behavior is documented, it seems odd.

Can anybody explain the reasons behind this design?

+6  A: 

Consider this

>> a=[0,1,2,3]
=> [0, 1, 2, 3]
>> a[0,10]
=> [0, 1, 2, 3]
>> a[1,10]
=> [1, 2, 3]
>> a[2,10]
=> [2, 3]
>> a[3,10]
=> [3]
>> a[4,10]
=> []
>> a[5,10]
=> nil

So a[4,10] is the slice between the 3 and the end of the array which is []

Where as a[4] and a[5,10] are accessing elements that aren't in the array

It may help to think of the slice points as being between the elements, rather than the elements themselves.

gnibbler
The first parameter is declared as the "start" position, which is already past the end of the array (and is why `a[a.length]` is `nil`). The example makes a lot less sense if you use anything other than the indexes as the elements.
Diego Mijelshon
@Diego Mijelshon, Changing the elements to `a=['a','b','c','d']` works exactly the same, the length of the result goes from 4 to 3 to 2 to 1 to 0 and finally `nil` is returned
gnibbler
So, it's just an implementation detail. No philosophy behind it? In Python, anything outside the range returns `[]`. In .NET, `ArraySegment` would throw, and Linq returns an empty sequence. (I'll still upvoting your answer, but I'm really disappointed at the language)
Diego Mijelshon
This looks like a bug to me, or a documentation error. The docs state that nil is returned if the index or starting index is outside of the range so a[4] and a[4,10] should both return nil
Steve Weet
I'm with Diego here - although I understand that the behaviour is as document, it feels inconsistent. To me (with a C/C++/C#) background, I'd expect the example to give either: [0,1,2,3], [1,2,3], [2,3], [3], [], [], ...Or [0,1,2,3], [1,2,3], [2,3], [3], nil, nil, ...
Steve Strong
It's a Lispy thing. The list (1 2) = (cons 1 (cons 2 nil)). The nil marks the end of a properly formed list. There is a chapter in SICP that explains it. Look at this question http://stackoverflow.com/questions/2921912/in-sicp-exercise-2-26-using-drscheme-why-does-cons-return-a-list-instead-of-a-p that explains a bit. There may be better ones out there somewhere. It is **not** inconsistent.
Allen
@Diego, It may help to think of the slice points as being _between_ the elements, rather than the elements themselves.
gnibbler
@gnibbler believe me, it's not that I don't get it. It just "feels" wrong (by wrong I mean error-prone). And if the semantics of a[x,y] are so different from a[x], a different operator should be used.
Diego Mijelshon
@Steve Weet, look at the special cases in the linked docs. It behaves as documented.
Diego Mijelshon
@Allen that feeds my impression that an implementation detail or a "legacy" artifact is playing an unnecessary role in the model.
Diego Mijelshon
@Diego Mijelshon No. It is part of the definition of the data structure you are all discussing. Without it, once the last element is removed, *you would not be left with the empty list*. Are you one of those people who believe that nil does not exist?
Allen
@Allen I do "believe" in nil (I call it null, though ;-)). But Ruby is supposed to be a high level language. I'm fine with having a \0 or \0\0 in C code, but I don't need terminators in C#, Java or Python because the supporting data structures abstract the implementation details.
Diego Mijelshon
@Diego M. Oops. I was talking about lists but everyone else is talking about **Array**! I have to agree now that this is curious. Sorry!
Allen
I was beating myself over this the other day while working with Ruby Koans. I found it really inconsistent and odd, but gnibbler's suggestion to "think of the slice points as being between the elements, rather than the elements themselves" really clears this all up for me. Thanks!
enriquein
@enriquein, I've added that to my answer now so people don't have to dig through the comments
gnibbler