4 ms·
>when you see `-1` in code it is often a failure of the expressiveness of 0-indexed lists. ...And when you see '+1' or '(x%ArrayLen)+1' or 'x<=arrayLen', in lu
by qwertyuiop924 10y ago
>when you see `-1` in code it is often a failure of the expressiveness of 0-indexed lists.
...And when you see '+1' or '(x%ArrayLen)+1' or 'x<=arrayLen', in lua or FORTRAN it is often a failure in the expressiveness of one-indexed lists.
- int_19h 10y agoThe most common case where +1 arises in 1-based indexing code is when you have to index from the end, but the language doesn't support a special syntax for it. So, for example, if you need to slice an array, starting from the Nth, and up to and including the Nth from the end, and your slice syntax is end-inclusive (as is typical for 1-based approach), you get something like: a[N : length(a) - N + 1] However, if you do have such special support - e.g. if negative indices are treated as from-the-end (like Python does), then it's just: a[N : -N] Compare this to 0-based N with end-exclusive slices (again, as is typical). If you don't have reverse indexing, it's slightly simpler: a[N : length(a)] but with reverse indexing (where -1 is the last element), it's actually worse: a[N : -N - 1] FWIW, most of it has more to do with the fact that 1) negative numbers are probably not the best way to represent reverse indices, and 2) depending on the situation, either end-inclusive or end-exclusive can be convenient. In fact, merely having a special syntax for reverse indexing solves it for 0-based languages. For example, suppose that we extend the slice syntax such that either index can be prefixed with ~ to indicate indexing from the end. Then ~0 is the last element, and it becomes: a[N : ~N] This has a side benefit of avoiding errors when the computed index is accidentally negative, and ends up being treated as reverse, even though that was not actually the intent. Encoding the direction in the slice syntax itself means no runtime magic. Alternatively, you can provide syntactic sugar for the length of the array within the indexer - say, `$`. Then, in 0-based end-exclusive approach, you just write: a[N : $ - N] This is one case that 1-based approach doesn't handle nearly as well, because for the same simple syntax to work there, you'd need to make $ be length + 1, which is kinda ugly. However, this only works if your ranges aren't first-class (i.e. if they only appear inside []). These days, the trend seems to be to have ranges as first-class values in the language, and have the indexer accept a range as an operand. In that case, since the range can be constructed separately, it needs to be able to encode "index from start" and "index from end" inside the object itself, and special range syntax for that makes more sense. If the language uses iterators or equivalent, then the iterator itself can encode the direction, and the range is just a pair of iterators. But you would still want some special short syntax, or at least a short convenience function, to easily create reverse iterators. C++ does just that - a pair of iterators expressing the same thing for random-access container `a` would be: make_pair(a.begin() + N, a.rbegin() + N) This is very generic, but the annoyance here is that you have to repeat `a` twice, and it could be a lengthy expression, or even have side effects. Some form of let..in syntax to introduce local bindings in expressions, like ML does, would help a lot here - you could do e.g.: let a = <some very long expression> in make_pair(a.begin() + N, a.rbegin() + N)
- Sean1708 10y agoI agree with 'x%ArrayLen+1', but how is 'x<=arrayLen' a failure? Also, I don't think the parent's point is "1 is better than 0" but rather "0 is not better than 1".