10 ms·
Why Python uses 0-based indexing. It's because of slices
- xentronium 13y agoI think ruby has it nailed better. a[1..10] # elements from 1 to 10, including 10 a[1...10] # elements from 1 to 9; k...n is a half open range notation a[3, 5] # 5 elements, starting from position 3 Arrays are still zero based, but I feel this is more a homage to for- and while- loops from older languages. (for (i = 0; i < n; i++) is just a very nice notation, and so is while (i < n))
- anonymoushn 13y agoIs it possible to nail something when the approach to a decision between two conventions is to implement all three?
- teraflop 13y agoFrom the perspective of someone who doesn't know Ruby, the fact that a[1..10] is longer than a[1...10] is pretty counterintuitive.
- xentronium 13y agop..q is a fairly common math notation synonymous with [p,q]. Although, p...q is not very obvious, indeed.
- marcosdumay 13y agoWell, they aren't synonymous in Ruby. It's [p,q] operator is also not intuitive for me. But then, "not intuitive" just means I'll spend a few minutes more learning the language because of it (except in C++, where it means many hours).
- kintamanimatt 13y agoI found it quite unintuitive too at first. My way of remembering it was to think of the distance between the endpoints. The first example is inclusive because it's closer and the endpoint is "included" whereas the second range endpoint is shunned and shoved further away and is exclusive because it's "excluded"!
- beaumartinez 13y agoRuby offers three very similar syntaxes for the same concept. Contrast that with Python's "there should be one—and preferably only one—obvious way to do it"[1] ethos. [1] http://www.python.org/dev/peps/pep-0020/ http://www.python.org/dev/peps/pep-0020/
- saraid216 13y agoRuby doesn't nail it using ranges. Ruby nails it by implementing Array#each. 0-index versus 1-index should not be an issue at this level of programming. You shouldn't be using for-loops; you should be iterating from start to finish.
- dazmax 13y agoAgreed. It seems messy at first, but what Ruby does is make it possible to write code in the most readable way. a[first..last] left, right = a[0...pivot], a[pivot...a.len] a[start, len] If the coder picks the right one and the variable names are good, the reader won't have to think about it.
- simias 13y agoI don't know anything about ruby but that seems extremely error prone to me. EDIT: I think I know why I think it's error prone: In all programming languages besides the core syntax of the language idioms appear among the community of coders. For instance, in C you travel through an array using something like: for (i = 0; i < n; i++) a[i] = ...; Where n is the length of the array. This is an idiom so common that if for some reason in some code the length of an array is (n + 1), instead of writing for (i = 0; i <= n; i++) I'll write for (i = 0; i < (n + 1); i++). Because the idiom is so strongly entrenched in my brain (and probably many other C coders) if I read the <= version there's a great chance I'll read it as a "<" and assume i goes from 0 to n - 1. My point is adding several alternative "syntactic sugar" to express the same thing can end up confusing newbies because they'll have to learn the difference between all version (and recognize them) as well as experienced coders because you're more likely to mistake the meaning of an expression at a cursory glance.
- draegtun 13y agoI have to agree with some of the others here and say that not all of the above is very intuitive. In particular a[3, 5]. I would be expecting it to return elements 3 & 5 (shortcut to a[3] & a[5]) instead of doing a length slice. For eg, in perl: my @a = qw/zero one two three four five six/; $a[3]; # "three" @a[3, 5]; # ("three", "five") @a[3, 0, 5]; # ("three", "zero", "five") @a[1..5]; # ("one", "two", "three", "four", "five") Also the ... operator is slightly at odds when compared against .. operator. I think perl6 has a more intuitive version: @a[1..^5]; # ["one", "two", "three", "four"] So instead of 1 to 5 (1..5) you think 1 upto 5 (1..^5).
- BoppreH 13y agoThere's a short article from Dijkstra on this reasoning: http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF He enumerates the four possible ways one could use, and goes on the pros and cos of each.
- oftenwrong 13y agoHis handwriting should be a font
- ginko 13y agohttp://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra.aspx http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijks...
- memracom 13y agoI don't trust sites that ask you to download an .EXE downloader. If you go here you can download it as a .TTF file http://www.fontpalace.com/font-download/Dijkstra/ http://www.fontpalace.com/font-download/Dijkstra/
- dllu 13y agoThe '1' in that font clearly differs from the one in http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF
- StavrosK 13y agoThe "w" as well.
- anonymoushn 13y agoOn OSX it just gives me a zip file with the font in it.
- 13y ago
- rogerbinns 13y agoI never have off by one errors in Python, but have always experienced it in previous languages and their libraries. Guido chose very well. (The one exception is a function in the random module.)
- calpaterson 13y agoI frequently have off-by-ones with range. len(range(1, 100)) == 99
- anonymoushn 13y agoYes, it's 100-1. len(range(25,100)) == 75 == 100-25
- aidos 13y agoSimplify it: len(range(100)) == 100 Though - I've written a lot of python code and I never use range. The beauty of python is that all collections are iterable so you seldom need to specify ranges.
- jmmcd 13y agoStupid randint. I have to look it up every time. And I have to look up numpy's randint as well.
- FBT 13y agorandrange is the fixed randint, which works just as you'd expect. It's far preferable to randint, for the aforementioned reason, and ever since I started using it all those annoying off by one errors that randint kept giving me vanished. The one thing I still use randint for is rolling dice. randint(1,6) is the equivalent of a six-sided die, and is that bit more readable as such than the equivalent using randrange. But for any other purpose, randrange beats randint hands down. Off-by-one errors, be gone!
- dpratt 13y agoThis is not meant to be tongue-in-cheek - I thought this argument was settled ages ago. Are there any respected languages that use 1 based indexing? This is an honest question - every single one I've ever used (C, C++, Java, Scala, Python, JS, Objective-C, a bunch of academic ones) have been zero based. It's quite clear that it's the right solution, since the first element of an array is zero distance away from the beginning of the array.
- deleted 13y ago[deleted]
- esrauch 13y agoLua is probably the strongest candidate for a respected 1-based language. Smalltalk, Fortran, and MATLAB are other examples of widely used languages that are 1 based.
- desireco42 13y agoI am going to learn Lua and use it much more now, thanks for pointing this out. As someone said, having 0 based indexes is inhuman way of dealing with arrays, only made some sense in C, others just copied.
- deleted 13y ago[deleted]
- theseoafs 13y agoOff the top of my head, Lua uses primarily 1-based arrays. (Of course, Lua arrays are actually associative arrays, so negative numbers and 0 are also valid indices, but all the standard library functions except your arrays to start at 1.)
- russellsprouts 13y agoLua. However, you are allowed to use 0 based indexing as well -- but the # length operator and table.remove() will not work correctly.
- Siecje 13y agoI don't think it is intuitive for my_list[0:3] to be the first three elements of the list. Go from position 0 to position 3 should be 0, 1, 2, 3
- ronaldx 13y agoAgreed. I find the half-open ranges impossible and stumble over them every time. They are inconsistent within two characters: closed on one side, then open on the other. If the notation was >my_list[0:3) at least it would be mathematically appropriate.
- Peaker 13y agoSee the Dijkstra essay[1] for a convincing argument for half-open ranges. https://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD831.html https://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD831...
- ronaldx 13y agoI have read it, and Dijkstra is a good call to authority, but I don't agree. His opening sentence is instructive: using the 'pernicious three dots', everything is totally clear: with an ellipsis, it's impossible to understand anything other than a closed interval. It's primary school mathematics that 2 <= x <= 12, i.e. the interval mathematically written [2,12] (or 2,...,12), contains 11 integers. A programmer who doesn't understand that is in a world of trouble as half-open intervals won't stop the inevitable rain of off-by-one errors. A programmer who actually calculates the size of a list in the way that Dijkstra suggests (specifically, by calculating b-a) is also perhaps acting questionably. Dijkstra's argument here might make more sense when correctly framed in 1982. As an aside, if Dijkstra would agree to use a fully-closed interval, his suggested advantage of 0-indexing is eliminated and he would presumably then prefer 1-indexing.
- Peaker 13y agoThat's not the entirety of the argument. There's the natural number stuff, as well. Also, b-a is appealing because simplicity is a good indicator you're on the right track, and part of the definition of elegance.
- shmageggy 13y agoThe post he mentions is a really good read. http://exple.tive.org/blarg/2013/10/22/citation-needed/ http://exple.tive.org/blarg/2013/10/22/citation-needed/ It was submitted here yesterday but got no love. https://news.ycombinator.com/item?id=6595521 https://news.ycombinator.com/item?id=6595521 edit: fixed link
- haberman 13y agoThe article linked from this plus page (http://exple.tive.org/blarg/2013/10/22/citation-needed/ http://exple.tive.org/blarg/2013/10/22/citation-needed/) poses the question "why do programmers start counting at zero?" and then shows contempt towards anyone who thinks they know the answer (while dismissing Dijkstra as "incoherent", without argument). To the author, the correct way to answer this question is by investigating the history of how things happened to end up the way they are, which (apparently) no one besides him has ever done. While history is interesting, I think the much more important question is: why ought we to count from zero (or not)? And the answer to that question has nothing to do with history. In addition to Dijkstra's argument, mentioned elsewhere in this thread, 0-based addressing is more efficient (since it can be used directly as an offset, without having to bias it first). As much as I like Lua, its 1-based indexing is not my favorite part of that language. In particular, the numeric for loop is confusing, because the loop: for i=x,y do -- stuff end ...will execute (y-x+1) times, not (y-x) as it would in most languages, like Python: for i in range(x,y): # stuff Off-by-one errors are some of the most annoying bugs. 0-based indexing and half-open ranges help avoid them in my experience.
- soperj 13y agoVBScript is 0 based for some stuff, but 1-based for slicing. Drove me nuts for a day until I figured it out inspite of the criptic error message. Hate VBScript for this reason alone.
- eridius 13y agoThere's another reason that I haven't see anyone bring up. If your native unsigned integer type is the same size as a word (which is common), and a word defines the limits of your address space, and if you have a byte pointer initialized to 0, then 0-based indexing allows you to index into every byte in your address space, whereas 1-based indexing prevents you from indexing the final byte in your address space. Additionally, assuming an unsigned integer type is used for indexing, 0-based makes every index valid and meaningful, whereas 1-based leaves one index value (0) as invalid.
- Dylan16807 13y ago
- knappador 13y agoUsing zero reminds you that black is 0, 0, 0 in 24bit integer rgb, while white is 255, 255, 255, not 256, since you only have 8 bits and 2^8 - 1 = 255, not 256, and calling black 1, 1, 1 makes no intuitive sense. Using zero indexes saves a tremendous numbers of errors by habituating on what is more natural for digital memory and the representation capability of intrinsics while creating a nice correspondence between hex zero, decimal zero, binary zero. The only time this leads to bugs in Python is when using my_list[len(my_list)] instead of my_list[len(my_list) - 1], where the difference between the magnitude of indexing vs counting can lead to intuitive error. However, it's easier to just write list[-1], knowing that zero is the first element, so counting backwards has to start at a different value. Of course you can do something silly like my_list[-len(my_list)] to just be silly. Indexing is about having some value to get some position out of an array. Zero is a unique number, so it makes sense to use it as an index. If you start counting with your fingers by holding up none and call it zero, suddenly life makes intuitive sense. If you count the numbers that you count on the way to ten fingers, you get eleven unique numbers for those ten fingers. The difference between array length and array indexing. Magic.
- michaelochurch 13y agoI like 0-based indexing because it makes mathematical sense. By the ordinal construction of the natural numbers, e.g. 5 ~= {0, 1, 2, 3, 4}. So an array of length 5 has indexes corresponding to the five set-theoretic elements of 5.
- rch 13y agoThis was nice to read. I only wish itertools count was available by default, in the same manner as range.
- jerf 13y agoI've spent a lot of time in both domains, and the Python-style half-open interval is the clear winner, because it produces the fewest "+1" and "-1", each and every one of which is begging to be a bug. I disagree with Dijkstra on some other counts, but in this case he is correct, both in theory and in practice. It's easier today than it used to be 1 indexed, though, because there are more languages that support some form of for element in collection: ... instead of the incredibly error-prone for (int i = 0; i < something; i++) { ... } Or, was it for (int i = 0; i <= something; i++) { ... } Or was it for (int i = 1; i <= something + 1; i++) { ...} You have fewer places to make fencepost errors when you use the first style, so the costs of 1-based indexing are fewer. They're still higher than Python-style, though, because in Python you have both the correct for loop and still have the correct intervals.
- to3m 13y agoWhen I started programming I quickly found that 0-based indexing was generally simpler. A lot of the time, it makes no difference one way or the other (because you're only using indexes to get at each item in turn, so you just memorize the boilerplate for whichever language you're using), but the moment you introduce ranges, or try to convert dimensionality - particularly with random access - or do some wraparound, or try to implement something like a ring buffer, it's all too easy to come a cropper. Either you put "1+" on every array access, which is error-prone, or you try to manage two different "spaces" (the 0-based one that's the ideal representation and the 1-based one used for indexing) and carefully convert between them... which is error-prone too. Alternatively, you could just decide to start indexing at zero. ...or you could stick with 1-based indexing, it's up to you. The right thing to do doesn't become wrong just because you don't do it ;)
- ot 13y agoAs Guido says, 1-based indexes basically force closed ranges, so that [1, length] is the full array. One thing that annoys me about closed ranges (and it's not much talked about) is that it is impossible to express the empty range: in python [i:i] is an empty range, while closed ranges always contain at least one element. That makes closed ranges strictly less expressive than half-open ranges, making often necessary to write special cases for zero-length. For example when you want to split an array or a string at a given position i, the partitions will always be a[:i] and a[i:] even if one of the two is empty. The other big issue, as many comment, is that the arithmetic to index multi-dimensional arrays is cumbersome with 1-based indexing: for example if I is a 2d image in row-major, the element (x, y) is at position I[(y - 1) * width + x] instead of I[y * width + x]. It's very easy to do off-by-one errors this way. EDIT: adding this just to try some ASCII art :) I think that the preference about 0-based and 1-based indexing boils down to how you reason about indexes. I (0-indexing) think of them as the following: |-|-|-|...|-| ^ ^ first past-the-end position that is, the index is where the element begins. This way there is a special endpoint, the "past-the-end", where the an appended element would begin. In particular, an empty array has both first and last endpoint equal to 0. I believe that people who favor 1-based indexing think like this |-|-|-|...|-| ^ ^ first last element element that is, in the ordinal way, i is the i-th element.
- deleted 13y ago[deleted]
- eieio 13y agoThis post reminds me of an excellent stack overflow answer[1] explaining how slices in python refer to the spaces between the elements. [1] http://stackoverflow.com/questions/509211/pythons-slice-notation/7315935#7315935 http://stackoverflow.com/questions/509211/pythons-slice-nota... (Perhaps in the context of this post I should index my first footnote as 0 :p)
- FaceKicker 13y ago> One thing that annoys me about closed ranges (and it's not much talked about) is that it is impossible to express the empty range: in python [i:i] is an empty range, while closed ranges always contain at least one element. That makes closed ranges strictly less expressive than half-open ranges, making often necessary to write special cases for zero-length. In matlab/octave you can write an empty range as a(i:i-1). Not to say that that's at all elegant or aesthetically appealing, but it does work. But I 100% agree with the sentiment; one of my favorite things about using NumPy rather than matlab in my research code is the vastly superior indexing, in large part due to the fact that it's 0-based. matlab's indexing really is painful and off-by-one-error prone for doing anything other than a(1:n).
- rtkwe 13y agoWhy is it zero indexed? Tradition. Why did that tradition start? The best explanation I can think of is because array access is a convenient short hand for pointer math: array[i] translates perfectly into array_memory_start + i*element size. Thus the tradition was most likely born.
- thisisnotatest 13y ago> was most likely born I take it you just posted this comment for fun and didn't RTFA? That was the whole point; the author answered the question of how the tradition was born, down to the person and year.
- rtkwe 13y agoI did read it. And all he says is that C was zero based but doesn't say why that happened. Unless it was in the comment thread or one of the several second order articles which I'll freely admit I didn't read.
- DigitalJack 13y ago1-based counting would make binary very uncomfortable. b0 would be d1 and b1 would be d2. You would have the strange situation of needing more than 1 bit to represent 0. Doesn't seem very intuitive to me, but that's due to familiarity more than anything I suppose.
- espeed 13y agoAh yes, but don't forget this is all changing in Python 4 :) -- remember the "List Revolution" thread when Guido finally agreed to switch to 1-based indexing... On Fri, Sep 9, 2011 at 2:12 PM, Christopher King <g.nius.ck at gmail.com> wrote: > The first element in a list is element zero, the second is one, the third it > two, and so on. This some times confuses newbies to the language or > programming in general. This system was invited when single bits where > precious. It's time to update. Keep in mind this is something for version 4, > since its not reverse compatible. I say we make the first element 1, second > 2, third 3, and so on. Other languages would follow. We are python, made for > easiness and readability, and we are in the age where you can't even get > something as small as a kilobyte USB. We must make first one, second 2, and > third 3, like it is supposed to be. I give this: > +1 Consider it done. -- --Guido van Rossum (python.org/~guido) Source: https://mail.python.org/pipermail/python-ideas/2011-September/011448.html https://mail.python.org/pipermail/python-ideas/2011-Septembe...
- ssafejava 13y agoFunny thread, but just in case somebody takes it seriously: (And to those taking the thread seriously: this is all in jest. We won't change the indexing base. The idea is so preposterous that the only kind of response possible is to laugh with it.) --Guido https://mail.python.org/pipermail/python-ideas/2011-September/011462.html https://mail.python.org/pipermail/python-ideas/2011-Septembe...
- frou_dh 13y agoWhile we're at it, the 0 key on a keyboard should be before the 1 key, not after the 9 key :-)
- U2EF1 13y ago> My compromise of .5 was rejected without, I thought, proper consideration.
- evanmoran 13y agoIt doesn't matter how cool the slice notation is when people have trouble accessing single elements! 1 based is better for human counting and 0 is better for certain mathematical expressions, usually when thinking about offset from the start rather then list position. Clearly it is a judgement call, but I personally think counting is more important for the same reason PI is wrong. We are confusing ourselves because easy things aren't intuitive. Imagine you have: var list = ['first','second', 'third'] `list[2]` should clearly be the second element (not third), just as PI / 2 should mean half a circle (not 1/4). We wonder why there aren't as many computer science / math grads. Well in every intro class in the world people are getting confused by this.
- IanCal 13y agoDepends on how you read `list[2]` "Start at the beginning and move two elements along" works perfectly well.
- analog31 13y agoI've been teaching Python to my kids (both in middle school), and had to explain zero based arrays. Fortunately, all explanations automatically translate into "yet another stupid thing that grown-ups do." ;-) Being somewhat of an old timer, I understand the justification of zero based arrays based on making the hardware simpler and maintaining a tight coupling between the representation of a program in source code and its implementation in hardware. Zero is special because all of the bits are the same. Arrays are zero based because the physical location of the first element is the same as the memory location assigned to the array itself, plus zero. For the last element, I just remember the mnemonic "up to but not including."
- vidarh 13y agoDrag out a ruler and ask them about distances. It's a simple way of addressing the difference: 0-indexed "points to" the start of each element. 1-indexed represents the ordinal position. So to relate to the ruler, the first cm/inch interval is, well, the 1st. But the distance to the start of the first is 0. So if you want to emphasise which numbered interval it is, 1-indexed makes most sense. If you want to emphasise where it starts, or the distance from origin, 0-indexed makes more sense.
- deleted 13y ago[deleted]
- nodata 13y agoHumans use 0-based indexing. You're not born one year old.