Counting From One |
|
|
---- I've made an attempt to patch on Lua 5.3.4 source distribution, including ALL official libraries: lua-base-0. Open-sourced[1]. If you are interested, please go to check (and port to the latest lua, write some test cases for the best) if I missed something. Some exceptions: In string.gmatch, "%0" is for the whole match, and "%1%2"... for the first, second captures. Unchanged.The definition of subscription of args table is unchanged, but note that the length operator will now return a number larger by 1.math.random is an uncertain point. The current status is most for practicability: it returns [0, n) when 1 argument is provided, [a, b] for 2.Numeric for loop is another. The current status is: {0,1,2} for for i=0,3 and {3,2,1} for for i=3,0,-1.I'm considering to make the latter {2,1,0} too, since it just works as backward iteration. But it'll just look ugly on definition, also some problem floating-point numbers may occur. (But I wonder, honestly, who have ever used it in production? ) What do you think? The border case of table.remove where i==#t+1 (i==#t base-0) is removed. I don't understand it. It raises an error now.The rest should be trivial, including some circumstances where negative indices are supported as reversed, where it'll work mostly like in python. Another point for my contrary to base-1 table: It causes problem and confusion when converting from/to JSON, which is a widely used data exchanging format. --farter |
There is exactly ONE spot in the core of Lua where this is true: the parsing of constructors such as {10,20,30} . The convention of starting with 1 is only enforced by the libraries, which are not part of the language. --lhf
From the Lua manual:
And aww, let's get over with this: Define a proxy table to the real table with the index metamethod overriden to access i-1 on the real table if i is positive. Decide whenever you'll map negative indices directly to the real table or if you'll further process them. Decide what you'll do with the index 0 on the real table.
That done, use the proxy table whenever you need to count from zero. Use the real table whenever you need library functions. For mnemonics, name your table 'tablename1' and the proxy 'tablename0'. Problem solved.
being both a C++ and a Lua programmer, i have to completely disagree with this rant. Counting from 1 is the "natural" way of counting. In math, everyone counts from 1, too. Counting from 0 is only good for the C convention that an array and a pointer are equivalent, and that a[i] is the same as *(a+i). Also, i think, it is easy for a programmer to adapt to the non-C-like way of counting. Yet it makes Lua much more intuitive for "casual programmers". --PeterPrade
+1's because Lua does not use half-open ranges, where as in C++ (especially when using STL, which uses that style of ranges in its entirety) this won't happen. In addition, I agree that counting from one is natural to humans (I never stated otherwise), however representing zero-length ranges with [n, n-1] is very awkward. It is not natural to communicate "take no steps forward" by indicating to someone a start point on the floor, and then a destination point one step behind. In contrast humans can easily understand "take no steps forward" by indicating a start point on the floor, and a destination being that same point-- corresponding with the half-open range [n, n). --JohnBelmonte
Sometimes half-open ranges allow concise coding and sometimes not. I've seen (and committed) plenty of fencepost errors with both half-open and closed ranges. The fact that the last element in a C array has the index one less than the length of the array, for example, can lead to a variety of -1's in code; it is certain that there are techniques for eliminating these, but I wouldn't say that my Lua code is any more littered with +1's than my C code is with -1's. With respect to half-open ranges, though, there is a serious problem: you need to be able to represent a quantity that is not in the range. Consequently, for example, the type of an index of a string of length 256 needs to be at least short, even though every valid index is a byte. Similarly, a half-open range descriptor to the end of a vector contains an address which is not included in the storage of the vector, and which may well a valid pointer to a different object; this gets complicated for conservative garbage collecting, for example. (At least one conservative garbage collector deliberately overallocates to compensate for this problem.) I'm not standing up for one or the other: both are valid, both have advantages, and both have disadvantages. --RiciLake
as with counting from one, i think closed ranges are much more intuitive than half-open ranges, at least when you're not talking about the special case of a zero length range. When i say "intuitive" i mean it is more natural for someone who has not been a programmer at least for some years. --PeterPrade
I have not counted them, but I think that a vast amount of programming languages start counting at zero... And as I have learned C as my first language, starting at one seems confusing to --in fact I wrote a bunch of bad Lua code with arrays starting at zero in mind --AdrianPerez
Although counting from zero has its advantages, I find counting from one much more natural, even when programming. It is not a problem for me to switch between Lua and C, as in the past I've switched a lot between Visual Basic and C --Anonymous
First is 1st, not 0th --Kazimir Majorinc
"I'm in the process of evaluating Lua for an embedded scripting language in my app., and everything I have seen up to this has been very positive, until I realized
Lua is counting things from index 1. Bad. Bad. Baaad. Bad enough to consider tossing the whole thing out the door. As for justification, even though there shouldn't
be a need of any :),: indices of an array of length N are a natural map to the ring of integers modulo N, that have a lot of nifty properties. For example, when you want
to access your array cyclically, you just do index = (index+1) %N. Doing the same thing with indices starting at 1 is a pain the neck. Also, it makes binding C routines to Lua utterly painful."
iterator+1 to access the table index. --DanHollis?
lua_createtable() API call which allows you to specify the array size of the table (precisely). You can also specify the hash size, but that part is always restricted to a power of 2. If you create a table with a specified array size, its array part will be precisely that size so long as you do not force the table to expand, so it will work fine if you know the size of the table in advance. Binding arrays of small atomic objects, like ints, is made slightly easier in 5.1 as well, because of the possibility of overriding the # operator, but to make the userdata really act like a Lua table, you need to modify the default definition of ipairs and possibly of unpack. I've put some code for the former on the Wiki at GeneralizedPairsAndIpairs, in case it is of use to anyone. --RiciLake
lua_Number set to double as default. In the same way, it might be interesting to define a constant lua_FirstIndex (for instance) set to 1 as default, to refer to the first index. Every part of Lua related with the first index of an array (for instance, ipair, foreachi, etc.) should use this constant. If a user wants to change this constant to 0 and recompile, he/she could use Lua tables beginning from 0 at his/her own risk of loosing compatibility. The same constant should be accessible from Lua to allow also scripts being portable or independent of the first value of index arrays. --Salvador Espana
{[0]=10, 20, 30} and
the table-library functions of lesser value. --Ulrich Hoffmann <uho@xlerb.de>
[n,n) but also adjacent ranges nicely add up -- [a,b)+[b,c)=[a,c) -- without excessive doubling at the ends. The resulting range is (b-a)+(c-b)=c-a -long, and any or both of the components can be zero-length. Also, one can consider `negative ranges' [m,n) where m>n and the addition of ranges still works, now being even possible to obtain a zero-length range from non-zero ones.
Now, consider numbers a, b, and x. A correct expression for `x is between a and b' in the sense that min(a,b)<=x<max(a,b) (a half-open range) is (a<=x)=(x<b). Were it a closed range (...<=x<=...), we wouldn't have had such a nice expression. -- Boyko Bantchev
As a script language its first priority is to be easy for scripters to use. An array of 5 elements that has the last element as number 5 is surely easier to explain to casual scripters, than that it finishes at element 4?
I have interfaced many client functions in my system to expose their workings to Lua. The question of zero-or-one based doesn't even apply in many cases. For instance, a lot use string keys (in which case the problem goes away), or they don't use an array of any sort.
If you want Lua to be a universal scripting language I certainly would not recommend a compile-time option, so that half the Lua scripts published work based on zero and half based on 1. You would be opening the doors to a nightmare doing that. I can't even see how that can work if you want to interface things like LuaSocket, LuaCom etc. These would be written assuming the current convention, that arrays start at 1, and many are supplied with precompiled binaries for Windows. These would either not work at all if you ran on a zero-based system, or the authors of each package would have to clutter their code with tests for what the base is, surely losing any advantage of changing it in the first place. -- NickGammon
double num = luaL_checknumber (L, 1); /* get first item on stack */
Thus, programmers who are interfacing Lua with C are very familiar with that convention - you have to be. Again, if you made arrays zero-based, would you change that too? In which case how do you get the last element from the stack (which is currently -1)?
Some of the posts above don't really mention if they are referring to strings as well. For instance, the first item in a string:
c = string.sub ("ABC", 1) --> "A"
Would you make that zero also? If so, how do you get the last item? -- NickGammon
Using -1 as an alias for the last element is perfectly consistent with half-open ranges (i.e. counting from zero). See Python. In fact, it is more consistent than with closed ranges. Consider for a moment that lists wrap around, and you can move from beginning to end and back the "short way". One left of position 0 (the beginning of the list) is -1 (the end of the list). In other words you can just subtract 1 from your position to move left, which is natural. If you start a list at 1 instead, it produces a strange gap of two positions between the beginning and end. To move to the left, you'd have to subtract one from your position, unless you were at position 1, in which case you'd subtract 2.
Yes, that is true. However given that the "deed is done" now, I suggest that applications that need the "count from zero" approach for mathematical or other reasons, simply define their own foreachi function to work around the current behaviour. After all, there is nothing stopping you from putting elements into position 0 of a table right now.
If you want to make a table constructor start at 0 do this:
t = { [0] = "a", "b", "c" } -- a is in position 0, b is in 1 and so on
-- NickGammon
Unfortunately #t evaluates to 2. Much of the problem arises from the confusion of numerals with ordinals. When indices are offsets, 0 is the offset of the first item in the array. If we were a bit more scrupulous in retaining the type information with the use of numbers in vernacular speech there would be less contention on this. In mathematics, realizing a non-negative integer n as the set of numbers from 0 to n-1 inclusive gives much neater formulae than identifying it with the set of numbers from 1 to n. -- GavinWraith
mtx[1][1]. Sequences maybe vary more[4]. Concerning math software, Mathcad by default 0-indexes (though it can be changed), while Matlab, Maple, and Mathematica 1-index. --DavidManura
#t (or, table.getn) is not defined as returning the number of elements in a table. For example:
t = { foo = 1, bar = 2 } ; print (table.getn (t)) --> 0
From the Lua manual:
My example is consistent with the definition. #t returns the index of the last item.
-- NickGammon
It is obvious we are not going to reach agreement here, however don't you think that is just slightly confusing? "Subscript n ... refers to element n+1?". At least in Lua, subscript n refers to element n.
-- NickGammon
IMHO counting from 1 is great. First element is 1, last element is -1. No need to remember "this is not math, this is not something natural, get used to it", like when you deal with indices in python.
-- muntyan
And Lua lets you store 0 in your integers -- so which element does it get you?
E x a c t l y -- JeanClaudeWippler
DIM arrayname(5 to 18)In Forth you can also start with any number you want to, like:
HERE 14 ALLOT 5 - CONSTANT arrayname
It looks like counting from one is a very old legacy from unary systems. We should have get rid of this when inventing the zero but unfortunately we did not.
--Zifre
Let's make a bet. We will go to a park and talk to random people. We will show them three stones in a line, and ask them to count the stones aloud. If any person says, "Zero, one, two", then you win. If everyone says, "One, two, three", then I win. I imagine that I would always win this bet. No one counts from zero, because it makes no sense. When the first element of an array is accessed with 0, then that number represents an offset, not an index. The question is not about counting, it's about whether arrays should use offsets or indices.
Using offsets rather than indices does have some advantages, of course. But it's hard to argue with this: If arrays use indices, then the "nth" element is accessed with the number "n". Do you want the fourth element? Then use 4. Want the tenth? Then use 10. That makes a lot of sense, and has its own set of nice properties. Unfortunately, many programmers are closed-minded on this issue, and somehow convince themselves of absurd statements like, "It makes a lot more sense to count from 0." (And then, in their own post, illustrate that they in fact count from one, just like everyone else on this planet.)
Let's make a second bet. After asking people to count the stones, we will say, "Please point to stone one". If anyone points to the middle stone, then you win the bet. Otherwise, I win. Again, I don't see how I would realistically ever lose this bet. -- Anonymous
Also, the statement that this: [a,b]+[b,c] should have been this: [a,b]+[b+1,c] is completely off, as the original proposition needed half-open intervals, in which case, it looks like this: [a,b)+[b,c)
Therefore, the complaint is completely off, as [a,b)+[b,c) does NOT include b twice. I am going to correct that part and delete the rant about [a,b]+[b+1,c], because it's pointless. --Tim
Some exceptions:
string.gmatch, "%0" is for the whole match, and "%1%2"... for the first, second captures. Unchanged.
args table is unchanged, but note that the length operator will now return a number larger by 1.
math.random is an uncertain point. The current status is most for practicability: it returns [0, n) when 1 argument is provided, [a, b] for 2.
for i=0,3 and {3,2,1} for for i=3,0,-1.
i==#t+1 (i==#t base-0) is removed. I don't understand it. It raises an error now.
The rest should be trivial, including some circumstances where negative indices are supported as reversed, where it'll work mostly like in python.
Another point for my contrary to base-1 table: It causes problem and confusion when converting from/to JSON, which is a widely used data exchanging format.
--farter