4 ms·
That particular EWD is one of my pet peeves, because of how it always pops up in discussion about array indexing. There are several situations where 1-based ind
by ufo 11mo ago
That particular EWD is one of my pet peeves, because of how it always pops up in discussion about array indexing. There are several situations where 1-based indexing is better, but which Dijkstra doesn't mention. For instance, one-based is much better for iterating backwards.
I think a compelling argument can be made that 0-based is better for offsets and 1-based is better for indexes, and that we should not think of both as the same thing.
https://hisham.hm/2021/01/18/again-on-0-based-vs-1-based-indexing/ https://hisham.hm/2021/01/18/again-on-0-based-vs-1-based-ind...
- bazoom42 11mo agoAnd Dijkstras argument is actually quite weak if you read carefully. But he has a certain way of writing which make it seem almost like a mathematical proof. And then he sprinkles some “only smart people agree with me” nerd baiting.
- adrian_b 11mo agoThis is a matter of opinion. I consider Dijkstra's arguments quite strong. Some decades ago, I have disassembled and studied Microsoft's Fortran compiler. The fact that Fortran uses 1-based indexing caused a lot of unnecessary complications in that compiler. After seeing firsthand the problems caused by 1-based indexing I have no doubt that Dijkstra was right. Yes, the compiler could handle perfectly fine 1-based indexing, but there really was no reason for all that effort, which should have been better spent on features providing a serious advantage for the programmer. The use of 1-based indexing and/or closed intervals, instead of consistently using only 0-based indexing and half-open intervals, are likely to be the cause of most off-by-one errors.
- adrian_b 11mo agoOne-based is not better for iterating backwards. Zero-based indexing is naturally coupled with using only half-open ranges. When using zero-based indexing and half-open ranges, accessing an array forwards, backwards or circularly is equally easy. In this case you can also do like in the language Icon, where non-negative indices access the array forwards, while negative indices access the array backwards (i.e. -1 is the index of the last element of the array, while 0 is the index of the first element). In languages lacking the Icon feature, you just have to explicitly add the length of the array to the negative index. There is absolutely no reason to distinguish offsets and indices. The less distinct kinds of things you need to keep in mind, the less chances for errors from using the wrong thing. Therefore one should not add extra kinds of things in a programming language, unless there is a real need for them. There are things that are missing from most languages and which are needed, e.g. separate types for signed integers, unsigned integers, modular numbers, bit strings and binary polynomials (instead of using ambiguous unsigned integers for all the 4 latter types, which prevents the detection of dangerous errors, e.g. unsigned overflow), but distinguishing offsets from indices is not a useful thing. Distinguishing offsets and indices would be useful only if the set of operations applicable to them would be different. However this is not true, because the main reason for using indices is to be able to apply arithmetic operations to them. Otherwise, you would not use numbers for indexing, but names, i.e. you would not use arrays, but structures (or hash tables, when the names used for access are not known at compile time).
- ufo 11mo agoThe problem is that half-open ranges work best when you the start is closed and the ending is open. In forward iteration we use [0,n) but for backwards iteration we have to use (-1, n-1] or [0,n-1], both of which are kinda clunky.
- adrian_b 11mo agoOne should always use a single kind of half-open range, i.e. with the start closed and the ending open. The whole point here is to use a single kind of range, without exceptions, in order to avoid the errors caused by using the wrong type of range for the context. For backwards iteration the right range is [-1,-1-n), i.e. none of those listed by you. Like for any such range, the number of accessed elements is the difference of the range limits, i.e. n, which is how you check that you have written the correct limits. When the end of the range is less than the start, that means that the index must be decremented. In some programming languages specifying a range selects automatically incrementing or decrementing based on the relationship between the limits. In less clever languages, like C/C++, you have to select yourself between incrementing and decrementing (i.e. between "i=0;i<n;i++" and "i=-1;i>-1-n;i--"). It is easy to remember the backwards range, as it is obtained by the conversion rules: 0 => -1 (i.e. first element => last element) and n => -n (i.e. forwards => backwards). To a negative index, the length of the array must be added, unless you use a programming language where that is done implicitly. In the C language, instead of adding the length of the array, one can use a negative index into an array together with a pointer pointing to one element past the array, e.g. obtained as the address of the element indexed by the length of the array. Such a pointer is valid in C, even if accessing memory directly through it would generate an out-of-range error, like also taking the address of any element having an index greater than the length of the array. The validity of such a pointer is specified in the standard exactly for allowing the access of an array backwards, using negative indices.
- braincat31415 11mo agoWhat would you do if your array is so large that it requires an unsigned int64 index?
- analog31 11mo agoIn my view, zero based is good for making hardware. Resetting a pointer to zero allows using the same circuit for each bit. Granted, that's an argument for hardware, not for languages, and even the hardware angle is probably long obsolete.