• Next Update v4 - March 2016 Update is out!
    1,930 replies, posted
[QUOTE=Bo98;50092076]Are you wanting to ignore or deny non-numeric keys? [url]http://www.lua.org/manual/5.3/manual.html#3.4.7[/url][/QUOTE] Ooh, I didn't know that! [QUOTE=FPtje;50092109]:snip: Fuck the terminology, though. Backwards compatibility.[/QUOTE] As far as the terminology goes, the link Bo provided is the definition. That answers everything. But more important is your last point - backwards compatibility. The [i]intention[/i] of the old code is to tell whether the key is JUST sequential or not. That means, no non-integer keys are allowed, and no holes. The problem is that the [i]implementation[/i] of it is unreliable. For convenience, here is the original code again: [lua]function table.IsSequential( t ) local i = 1 for key, value in pairs( t ) do if ( !tonumber( i ) || key != i ) then return false end i = i + 1 end return true end[/lua] And, once again, my code which doesn't change the [i]intention[/i] of the function, only makes it consistent and reliable (minor changes from my last post): [lua]function table.IsSequential( t ) local max, count = 0, 0 for k in pairs( t ) do if !isnumber( k ) then return false end if k < 1 then return false end if (k % 1) != 0 then return false end -- non-integer key if k > max then max = k end count = count + 1 end return count == max end[/lua] If we want to change its meaning to match that of the Lua manual, [b]which we don't,[/b] we just need to change one "return false" to "continue": [lua]function table.IsSequential( t ) local max, count = 0, 0 for k in pairs( t ) do if !isnumber( k ) then continue end if k < 1 then return false end if (k % 1) != 0 then return false end -- non-integer key if k > max then max = k end count = count + 1 end return count == max end[/lua]
The rules for what is considered a sequential table are already defined in the # operator, and as pointed out, the manual. It's not up for interpretation.
[QUOTE=Neat-Nit;50092160] If we want to change its meaning to match that of the Lua manual, [b]which we don't,[/b][/QUOTE] Why not? After all, that is the official definition of IsSequential. It is both the best defined and most useful definition of "sequential". After all, with the manual definition you know definitively whether you can use ipairs. Also, the last snippet of code is wrong: it doesn't take non-number keys into account when counting the table.
[QUOTE=FPtje;50092184]Why not? After all, that is the official definition of IsSequential. It is both the best defined and most useful definition of "sequential". After all, with the manual definition you know definitively whether you can use ipairs.[/quote]In your own words: Fuck the terminology, though. Backwards compatibility. [QUOTE=FPtje;50092184]Also, the last snippet of code is wrong: it doesn't take non-number keys into account when counting the table.[/QUOTE] It's not wrong. The count is to be compared to max, and is only applicable to number keys. Everything else is ignored and irrelevant.
[QUOTE=Neat-Nit;50092200]In your own words: Fuck the terminology, though. Backwards compatibility.[/QUOTE] Are people relying on a broken implementation of not ignoring non-numeric keys?
Alright, good points. Would it be more efficient to define this: [lua]function table.IsSequential( t ) local max, count = 0, 0 for k in pairs( t ) do if !isnumber( k ) then return false end if k < 1 then return false end if (k % 1) != 0 then return false end -- non-integer key if k > max then max = k end count = count + 1 end return count == max end[/lua] as this? [lua] function table.IsSequential(t) return #t == table.Count(t) end [/lua] It denies non-integer keys and keys < 1, so that behaviour is the same. The only possible problem is the overridable nature of #. With a custom __len metamethod implementation could change behaviour. Is that desirable? One could argue that changing the rules of # (by overriding __len) should also change the rules of sequentiality.
# is just garbage anyway, i don't see the point of using it since it can return wrong values if it's non sequential, and you don't always know that
[QUOTE=Giraffen93;50092254]# is just garbage anyway, i don't see the point of using it since it can return wrong values if it's non sequential, and you don't always know that[/QUOTE] Most library functions return sequential tables.
[QUOTE=Giraffen93;50092254]# is just garbage anyway, i don't see the point of using it since it can return wrong values if it's non sequential, and you don't always know that[/QUOTE] I abuse that property to judge strict sequentiality. Also, about # being garbage: know that # is O(1), whereas table.Count is O(n).
[QUOTE=FPtje;50092289]I abuse that property to judge strict sequentiality.[/QUOTE] Take the example from the last page: [lua] local t = {} for i = 1, 20 do table.insert(t, i) end for i = 1, 19 do idx = math.random(i, 20) v = t[idx] t[idx] = t[i] t[i] = v end local newTbl = {} for k, v in ipairs(t) do newTbl[v] = true end newTbl[2] = nil -- CHANGE THIS INDEX print(#newTbl) [/lua] In the Lua demo this printed 20. Change the second last line to something like newTbl[16] = nil. Suddenly it switches to 15. The length operator is unreliable for non-sequential tables. This may be an extreme scenario but it shows how simply changing the index you are removing from the table can completely change the output of the length operator from ignoring holes to stopping at them.
[QUOTE=FPtje;50092245]Alright, good points. Would it be more efficient to define this: [lua]function table.IsSequential( t ) local max, count = 0, 0 for k in pairs( t ) do if !isnumber( k ) then return false end if k < 1 then return false end if (k % 1) != 0 then return false end -- non-integer key if k > max then max = k end count = count + 1 end return count == max end[/lua] as this? [lua] function table.IsSequential(t) return #t == table.Count(t) end [/lua] It denies non-integer keys and keys < 1, so that behaviour is the same. The only possible problem is the overridable nature of #. With a custom __len metamethod implementation could change behaviour. Is that desirable? One could argue that changing the rules of # (by overriding __len) should also change the rules of sequentiality.[/QUOTE] Quite the contrary, it would be less efficient. table.Count is guaranteed to go over the entire table, whereas the first snippet would stop the loop as soon as it finds a non-conforming key. As for the # debate, a quick google explains it better than I could: [url]http://stackoverflow.com/questions/23590885/why-does-luas-length-operator-return-unexpected-values[/url] [quote]The result of # operator on non-sequences is undefined.[/quote] What this means is that we [i]cannot[/i] claim that the result of # has any connection [i]whatsoever[/i] to the table, unless it's sequential. Taking the code in the question, what happens if I do this? [lua]t = {} t[1] = 2 t[2] = 3 t[4] = 3 t[400] = 400 print(table.IsSequential(t))[/lua] Tell me, FPtje, what does yours give and what does mine give. (I'm not trying to be a dick, I actually can't test it right now, but I am [i]asserting[/i] that mine will give the correct result while yours will not)
[QUOTE=FPtje;50092289]Also, about # being garbage: know that # is O(1), whereas table.Count is O(n).[/QUOTE] i'll take that performance hit over losing data
[QUOTE=Bo98;50092306]The length operator is unreliable for non-sequential tables.[/QUOTE] Oh, I didn't think it would give arbitrary results. That renders my method invalid. I expected #newTbl in your example to give 1, because that's the highest successive member from 1 on that has no holes. [editline]8th April 2016[/editline] [QUOTE=Neat-Nit;50092318] Tell me, FPtje, what does yours give and what does mine give. (I'm not trying to be a dick, I actually can't test it right now, but I am [i]asserting[/i] that mine will give the correct result while yours will not)[/QUOTE] The answer to this question is now irrelevant, since my method has already been proven to be wrong. [editline]8th April 2016[/editline] [QUOTE=Giraffen93;50092322]i'll take that performance hit over losing data[/QUOTE] You won't lose data if you use # in a disciplined matter, i.e. use it only on true arrays. Although the intricacies of the operator make its behaviour quite confusing.
32 bits in 64-bit SteamID are basically wasted - they are always the same. Instead of storing 64-bit number in text (ugh, terrible), you can just use 32-bit unsigned number. It is exactly the same SteamID people are used to ('STEAM_0:x:xxxxxxxx'), but in number form. This number has always existed, but usually it was represented in STEAM_0:x:xxxxxxx form. Also this number is what Valve have in their new SteamID ('[U:1:xxxxxxxx]'). It was already mentioned here as SteamID3. [code]function string:sid() local a, b = self:match("STEAM_0:([01]):(%d+)") return a and b and a + b * 2 or 0 end function sid(n) return n == 0 and "" or ("STEAM_0:%d:%d"):format(n % 2, math.floor(n / 2)) end function Player:sid() self._sid = self._sid or self:SteamID():sid() return self._sid end[/code] [QUOTE=Giraffen93;50092254]# is just garbage anyway, i don't see the point of using it since it can return wrong values if it's non sequential, and you don't always know that[/QUOTE] [QUOTE=Giraffen93;50092322]i'll take that performance hit over losing data[/QUOTE] It is not garbage at all. If you know your table is sequential, you should absolutely use '#'. 'table.Count' is way slower. Knowing if your table is sequential or not (and making sure it stays sequential) is part of your job.
i use { key = "value" } more often than not, so i almost exlusively use table.count
[QUOTE=Giraffen93;50092413]i use { key = "value" } more often than not, so i almost exlusively use table.count[/QUOTE] Sure, but it depends on the point of the table. A table where only the values matter and the keys are arbitrary, for example player.GetAll() or ents.GetAll(), benefits from being an array because you can get a quick player count by just doing #player.GetAll(). Sorted tables, where say the players are sorted by play time sequentially, can also use the # operator. But key-value tables obviously will never work with it, even if the keys are numbers. A nice rule-of-thumb is that if you exclusively use table.insert with a table, you can use #t on it. I made a library which generates shapes in vectors (for my soft lights), works like this: [lua]local radius = 10 local layers = 2 -- more layers = more vectors, but the actual number of resulting vectors depends on the shape used local vecs = vectorshapes.MakeShape("hexagon", radius, layers) -- vecs: -- layers: -- 1: -- 1: vector A -- 2: -- 1: vector B -- 2: vector C -- 3: vector D -- 4: vector E -- 5: vector F -- 6: vector G -- all: -- 1: vector A -- 2: vector B -- 3: vector C -- 4: vector D -- 5: vector E -- 6: vector F -- 7: vector G[/lua] So as you can see, I can use the # operator in loads of places here: [lua]#vecs.all -- how many vectors were generated overall #vecs.layers -- how many layers were generated #vecs.layers[i] -- how many vectors are in layer i #vecs -- meaningless, probably returns 0[/lua]
[QUOTE=Neat-Nit;50092448]because you can get a quick player count by just doing #player.GetAll()[/QUOTE] yes yes on built in functions that is on the topic of this, is there no way to get the [I]real[/I] player count (including connecting clients)?
[QUOTE=Giraffen93;50092495]yes yes on built in functions that is[/QUOTE]What's that supposed to mean? Did you not see my real-world example with vectorshapes?
[QUOTE=Neat-Nit;50092318]Whole lot of code[/QUOTE] Try this one [LUA]function table.IsSequential( t ) local c = 0 for k, v in pairs( t ) do c = c + 1 end for i = 1, c do if t[ i ] == nil then return false end end return true end[/LUA] My current test: [LUA]function table_IsSequential1( t ) local max, count = 0, 0 for k in pairs( t ) do if !isnumber( k ) then return false end if k < 1 then return false end if (k % 1) != 0 then return false end -- non-integer key if k > max then max = k end count = count + 1 end return count == max end function table_IsSequential2( t ) local c = 0 for k, v in pairs( t ) do c = c + 1 end for i = 1, c do if t[ i ] == nil then return false end end return true end local tab = {} for i = 1, 10000 do tab[ i ] = true end tab[ 99 ] = nil local t = SysTime() for i = 1, 10000 do table_IsSequential1( tab ) end print( SysTime() - t, table_IsSequential1( tab ) ) t = SysTime() for i = 1, 10000 do table_IsSequential2( tab ) end print( SysTime() - t, table_IsSequential2( tab ) ) [/LUA] for me this prints out: 4.3356848114331 false 0.50720978867139 false If you have other findings please let me know.
[QUOTE=Giraffen93;50092495]yes yes on built in functions that is on the topic of this, is there no way to get the [I]real[/I] player count (including connecting clients)?[/QUOTE] Not built in no, but you could use the player_connect event to grab those players and add them to a table (and likewise remove via the player_disconnect event).
[QUOTE=darkjacky;50092642]Try this one [LUA]function table.IsSequential( t ) local c = 0 for k, v in pairs( t ) do c = c + 1 end for i = 1, c do if t[ i ] == nil then return false end end return true end[/LUA] My current test: [LUA]function table_IsSequential1( t ) local max, count = 0, 0 for k in pairs( t ) do if !isnumber( k ) then return false end if k < 1 then return false end if (k % 1) != 0 then return false end -- non-integer key if k > max then max = k end count = count + 1 end return count == max end function table_IsSequential2( t ) local c = 0 for k, v in pairs( t ) do c = c + 1 end for i = 1, c do if t[ i ] == nil then return false end end return true end local tab = {} for i = 1, 10000 do tab[ i ] = true end tab[ 99 ] = nil local t = SysTime() for i = 1, 10000 do table_IsSequential1( tab ) end print( SysTime() - t, table_IsSequential1( tab ) ) t = SysTime() for i = 1, 10000 do table_IsSequential2( tab ) end print( SysTime() - t, table_IsSequential2( tab ) ) [/LUA] for me this prints out: 4.3356848114331 false 0.50720978867139 false If you have other findings please let me know.[/QUOTE] Interesting. Can you also check the results when a table has a lot of string keys? And a table that has both?
[QUOTE=Neat-Nit;50092662]Interesting. Can you also check the results when a table has a lot of string keys? And a table that has both?[/QUOTE] Sure at that point yours becomes infinity faster. [CODE] local tab = {} for i = 1, 10000 do tab[ tostring( i ) ] = true end[/CODE] 0.0017878918382488 false 1.0815355916573 false
[QUOTE=darkjacky;50092676]Sure at that point yours becomes infinity faster. [CODE] local tab = {} for i = 1, 10000 do tab[ tostring( i ) ] = true end[/CODE] 0.0017878918382488 false 1.0815355916573 false[/QUOTE] try:[lua]local tab = {} for i = 1, 10000 do tab[ i ] = true tab[ tostring(i) ] = true end tab[ 99 ] = nil[/lua] and:[lua]local tab = {} for i = 1, 10000 do tab[ i ] = true end tab[ 99 ] = nil tab.justone = true[/lua] (also try putting the justone above the for loop)
[QUOTE=Neat-Nit;50092543]What's that supposed to mean? Did you not see my real-world example with vectorshapes?[/QUOTE] meh i don't do that sorta stuff [QUOTE=Teddi Orange;50092654]Not built in no, but you could use the player_connect event to grab those players and add them to a table (and likewise remove via the player_disconnect event).[/QUOTE] yeah that's what i'm doing now, but it feels hacky like everything else in gmod :^)
[QUOTE=Neat-Nit;50092715]try and try[/QUOTE] [lua]local tab = {} for i = 1, 10000 do tab[ i ] = true end tab[ 99 ] = nil tab.justone = true[/lua] 4.3979245709177 false 0.50407402943015 false [lua]local tab = {} for i = 1, 10000 do tab[ i ] = true tab[ tostring(i) ] = true end tab[ 99 ] = nil[/lua] 4.4286920507713 false 1.57878339095 false [CODE]local tab = {} tab.justone = true for i = 1, 10000 do tab[ i ] = true end tab[ 99 ] = nil[/CODE] 4.344652064721 false 0.51358370645689 false
[QUOTE=darkjacky;50092642]Try this one [LUA]function table.IsSequential( t ) local c = 0 for k, v in pairs( t ) do c = c + 1 end for i = 1, c do if t[ i ] == nil then return false end end return true end[/LUA] [/QUOTE] Could you not combine those two loops? You are already counting from 1 to c by adding it up in the pairs loop so you could just move the nil check to right after you increment c. [LUA]function table.IsSequential(t) local c = 0 for k, v in pairs(t) do c = c + 1 if t[c] == nil then return false end end return true end[/LUA]
[QUOTE=Bo98;50092986]Could you not combine those two loops? You are already counting from 1 to c by adding it up in the pairs loop so you could just move the nil check to right after you increment c.[/QUOTE] Yes you could. Well spotted.
[QUOTE=Bo98;50092986]Could you not combine those two loops? You are already counting from 1 to c by adding it up in the pairs loop so you could just move the nil check to right after you increment c. [LUA]function table.IsSequential(t) local c = 0 for k, v in pairs(t) do c = c + 1 if t[c] == nil then return false end end return true end[/LUA][/QUOTE] BOOM!! This is the winner!
[QUOTE=Bo98;50092986][LUA]function table.IsSequential(t) local c = 0 for k, v in pairs(t) do c = c + 1 if t[c] == nil then return false end end return true end[/LUA][/QUOTE] -i'm wrong-
[QUOTE=>>oubliette<<;50093455]Probably overdoing it now, but could we also add in this to avoid more iteration than necessary? [lua] if (c > #t) then return false; end [/lua][/QUOTE] That's already covered by t[c] == nil. Think about it.
Sorry, you need to Log In to post a reply to this thread.