Lua 4 "n" property of tables - lua

In Lua 4, many tables have an "n" property which tracks the number of items inside the table.
Do all tables have this property?
Can it be overridden?
I ask, because I'm trying to develop a routine that prints all of a table's elements recursively in valid Lua syntax, and want to know if it's safe to filter all "n" items out of the result?
Thanks.
[edit]
Here's the script:
-- ThoughtDump v1.4.0
-- Updated: 2017/07/25
-- *****************
-- Created by Thought (http://hw2.tproc.org)
-- Updated by Mikali
-- DESCRIPTION
-- ***********
-- Parses the globals table and __TDPrints its contents to "HW2.log".
-- Can also be used to parse (i.e., pretty-print) generic tables in some cases.
-- Note: functions & variables must actually be declared in order to be parsed.
-- Otherwise, they are ignored.
-- Note: if parsing a table other than the globals table, the __TDPrinted table
-- values may be in a different order than was originally written. Values with
-- numerical indices are moved to the "top" of the table, followed by values
-- with string indices, followed by tables. Functions appear in different
-- locations, depending on whether they are indexed using a number or a string.
-- Note: despite the fact that nil values cannot be stored in tables, they are
-- still handled.
-- Note: even though functions may be referenced within tables, a function will
-- only be parsed correctly if it is indexed using a string that is the same as
-- the name of the function.
__TDOutputString = ""
function __TDParse(name, value, level, verbose, numbers, collapse)
if ((name == "__TDParse") or (name == "__TDSortHash") or (name == "__TDPrint") or (name == "__TDPrintGlobals()") or (name == "__TDOutputString")) then
return
end
local Element = nil
local ValType = type(value)
local NamType = type(name)
local PreLevel = ""
if (collapse == 0) then
for i = 1, level do
PreLevel = PreLevel .. "\t"
end
end
local ComLevel = ""
if (level ~= 0) then
ComLevel = ","
end
if ((ValType == "function") or (ValType == "userdata")) then
if (NamType == "string") then
Element = PreLevel .. name .. " = " .. name .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = " .. name .. ComLevel
else
Element = PreLevel .. name .. ComLevel
end
elseif (ValType == "string") then
if (NamType == "string") then
Element = PreLevel .. name .. " = \"" .. value .. "\"" .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = \"" .. value .. "\"" .. ComLevel
else
Element = PreLevel .. "\"" .. value .. "\"" .. ComLevel
end
elseif (ValType == "number") then
if (NamType == "string") then
Element = PreLevel .. name .. " = " .. value .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = " .. value .. ComLevel
else
Element = PreLevel .. value .. ComLevel
end
elseif (ValType == "table") then
if (NamType == "string") then
Element = PreLevel .. name .. " ="
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] ="
else
Element = ""
end
elseif (ValType == "nil") then
if (NamType == "string") then
Element = PreLevel .. name .. " = nil" .. ComLevel
elseif (numbers == 1) then
Element = PreLevel .. "[" .. name .. "] = nil" .. ComLevel
else
Element = PreLevel .. "nil" .. ComLevel
end
else
Element = PreLevel .. "-- unknown object type " .. ValType .. " for object " .. name
end
if (verbose == 1) then
Element = Element .. " -- " .. ValType .. ", tag: " .. tag(value)
end
if (((ValType == "table") and (NamType == "number") and (numbers == 0)) or (collapse == 1)) then
__TDPrint(Element, 0)
else
__TDPrint(Element, 1)
end
if (ValType == "table") then
__TDPrint(PreLevel .. "{", collapse == 0)
__TDSortHash(__TDParse, value, level + 1, verbose, numbers, collapse)
__TDPrint(PreLevel .. "}" .. ComLevel, 1)
end
end
function __TDSortHash(func, tabl, level, verbose, numbers, collapse)
local typesarray = {}
local typescount = {}
local keycount = 1
local keyarray = {}
for i, iCount in tabl do
local thistype = type(iCount)
if not (typesarray[thistype]) then
typescount[thistype] = 0
typesarray[thistype] = {}
end
typescount[thistype] = typescount[thistype] + 1
typesarray[thistype][typescount[thistype]] = i
end
sort(typesarray)
for i, iCount in typesarray do
sort(iCount)
for j, jCount in iCount do
keyarray[keycount] = tostring(jCount)
keycount = keycount + 1
end
end
for i, iCount in keyarray do
local tempcount = tonumber(iCount)
if (tempcount) then
iCount = tempcount
end
func(iCount, tabl[iCount], level, verbose, numbers, collapse)
end
end
function __TDPrint(instring, newline)
__TDOutputString = __TDOutputString .. instring
if (newline == 1) then
__TDOutputString = __TDOutputString .. "\n"
end
end
function __TDPrintGlobals()
__TDOutputString = ""
__TDPrint("globals =", 1)
__TDPrint("{", 1)
__TDSortHash(__TDParse, globals(), 1, 0, 0, 0)
__TDPrint("}\n", 1)
local WriteFile = "$test_globals_write.lua"
writeto(WriteFile)
write(__TDOutputString)
writeto()
end
__TDPrintGlobals()

In Lua 4.x in tables, n is just an element of the table like any other elements a table could contain, but it's not part of the table mechanism itself.
So, it can be overwritten or removed.
Some functions use it, like tinsert() ,and other table functions:
local tbl = { n=0 }
tinsert(tbl, 123)
print(tbl.n) --> 1
This is very useful as the getn() function gives only the highest number index of the table. But if there is only named elements in the table or a mix or number indexes and named indexes, then getn() doesn't reflect the real number of elements in the table. If elements are always inserted (or removed) using table functions like tinsert() then n is the accurate number of elements in the table.
Lua 4.x --> Lua 5.x equivalent:
getn(tbl) #tbl
tinsert(tbl,e) table.insert(tbl,e) or tbl:insert(e)
Of course you can still add elements in a table using the simple table access. But as n can be very useful, try to keep it updated as well.
tbl["Bla"] = 234
tbl.Bli = 345
tbl.n = tbl.n + 2
If n doesn't exist in a table but is needed by the code somewhere, it can be added using the for loop:
local tbl = {1,2,3,4,5,6}; tbl.a=11; tbl.b=22; tbl.c=33
local n = 0
for ie, e in tbl do
n = n + 1
end
tbl.n = n
or the foreach loop:
local tbl = {1,2,3,4,5,6}; tbl.a=11; tbl.b=22; tbl.c=33
tbl.n = 0
foreach(tbl, function() %tbl.n = %tbl.n + 1 end )
Note 1: The initialization of tbl.n to 0 will give the number of elements in the table, including n. Here the result of tbl.n is 10.
As eventually we don't want n to be counted as a real element of the table (which is is) but only counting other elements, we should init n to -1 then.
Note 2: The Lua 4.x upvalue operator % is used here because the tbl variable is not reachable in the function (not in the scope) for the foreach loop. It can be reached using %tbl. However, a upvalue is always read only, so the tbl variable can't be changed. The following will generate an error in the function:
%tbl = { } -- change the reference to another table
%tbl = 135 -- change the ref to the table for a number (or a string, ...)
As tbl variable contains in fact a reference to a table, the referenced table can be modifiable and therefore the element n can be changed without problem (as well as other elements of the table).
%tbl.n = %tbl.n + 1 -- increment the element n of the referenced table
Note 3: A global variable tbl could have been used, but it's good practice to always use local variable. Access to local variables is also faster than global.

Not all tables have this property.
It can be overwritten.
Why not traverse the table using a for loop? Or if possible, use Lua 5.3 ;)
In Lua this was called table for loop, in modern Lua it's called generic for loop.
The table for statement traverses all pairs (index,value) of a given
table. It has the following syntax:
stat ::= for name `,' name in exp1 do block end
A for statement like
for index, value in exp do block end
is equivalent to the code:
do
local _t = exp
local index, value = next(t, nil)
while index do
block
index, value = next(t, index)
end
end
Note the following:
_t is an invisible variable. The name is here for explanatory purposes only.
The behavior is undefined if you assign to index inside the block.
The behavior is undefined if you change the table _t during the traversal.
The variables index and value are local to the statement; you cannot use their values after the for ends.
You can use break to exit a for. If you need the value of index or value, assign them to other variables before breaking.
The order that table elements are traversed is undefined, even for numerical indices. If you want to traverse indices in numerical order,
use a numerical for.
Refer to the Lua manual 4.4.4
https://www.lua.org/manual/4.0/manual.html#4.4

Related

Generating all combinations from a table in Lua

I'm trying to iterate through a table with a variable amount of elements and get all possible combinations, only using every element one time. I've landed on the solution below.
arr = {"a","b","c","d","e","f"}
function tablelen(table)
local count = 0
for _ in pairs(table) do
count = count + 1
end
return count
end
function spellsub(table,start,offset)
local str = table[start]
for i = start+offset, (tablelen(table)+1)-(start+offset) do
str = str..","..table[i+1]
end
return str
end
print(spellsub(arr,1,2)) -- Outputs: "a,d,e" correctly
print(spellsub(arr,2,2)) -- Outputs: "b" supposed to be "b,e,f"
I'm still missing some further functions, but I'm getting stuck with my current code. What is it that I'm missing? It prints correctly the first time but not the second?
A solution with a coroutine iterator called recursively:
local wrap, yield = coroutine.wrap, coroutine.yield
-- This function clones the array t and appends the item new to it.
local function append (t, new)
local clone = {}
for _, item in ipairs (t) do
clone [#clone + 1] = item
end
clone [#clone + 1] = new
return clone
end
--[[
Yields combinations of non-repeating items of tbl.
tbl is the source of items,
sub is a combination of items that all yielded combination ought to contain,
min it the minimum key of items that can be added to yielded combinations.
--]]
local function unique_combinations (tbl, sub, min)
sub = sub or {}
min = min or 1
return wrap (function ()
if #sub > 0 then
yield (sub) -- yield short combination.
end
if #sub < #tbl then
for i = min, #tbl do -- iterate over longer combinations.
for combo in unique_combinations (tbl, append (sub, tbl [i]), i + 1) do
yield (combo)
end
end
end
end)
end
for combo in unique_combinations {'a', 'b', 'c', 'd', 'e', 'f'} do
print (table.concat (combo, ', '))
end
For a tables with consecutive integer keys starting at 1 like yours you can simply use the length operator #. Your tablelen function is superfluous.
Using table as a local variable name shadows Lua's table library. I suggest you use tbl or some other name that does not prevent you from using table's methods.
The issue with your code can be solved by printing some values for debugging:
local arr = {"a","b","c","d","e","f"}
function spellsub(tbl,start,offset)
local str = tbl[start]
print("first str:", str)
print(string.format("loop from %d to %d", start+offset, #tbl+1-(start+offset)))
for i = start+offset, (#tbl+1)-(start+offset) do
print(string.format("tbl[%d]: %s", i+1, tbl[i+1]))
str = str..","..tbl[i+1]
end
return str
end
print(spellsub(arr,1,2)) -- Outputs: "a,d,e" correctly
print(spellsub(arr,2,2)) -- Outputs: "b" supposed to be "b,e,f"
prints:
first str: a
loop from 3 to 4
tbl[4]: d
tbl[5]: e
a,d,e
first str: b
loop from 4 to 3
b
As you see your second loop does not ran as the start value is already greater than the limit value. Hence you only print the first value b
I don't understand how your code is related to what you want to achieve so I'll leave it up to you to fix it.

Trying to make function which takes string as input and returns no. of words in whole string

**It takes Input as a string such as this - 'Nice one'
And Output gives - 4,3 (which is no. Of words in sentence or string)
**
function countx(str)
local count = {}
for i = 1, string.len(str) do
s = ''
while (i<=string.len(str) and string.sub(str, i, i) ~= ' ' ) do
s = s .. string.sub(str, i, i)
i = i+1
end
if (string.len(s)>0) then
table.insert(count,string.len(s))
end
end
return table.concat(count, ',')
end
You can find a simple alternative with your new requirements:
function CountWordLength (String)
local Results = { }
local Continue = true
local Position = 1
local SpacePosition
while Continue do
SpacePosition = string.find(String, " ", Position)
if SpacePosition then
Results[#Results + 1] = SpacePosition - Position
Position = SpacePosition + 1
-- if needed to print the string
-- local SubString = String:sub(Position, SpacePosition)
-- print(SubString)
else
Continue = false
end
end
Results[#Results + 1] = #String - Position + 1
return Results
end
Results = CountWordLength('I am a boy')
for Index, Value in ipairs(Results) do
print(Value)
end
Which gives the following results:
1
2
1
3
def countLenWords(s):
s=s.split(" ")
s=map(len,s)
s=map(str,s)
s=list(s)
return s
The above functions returns a list containing number of characters in each word
s=s.split(" ") splits string with delimiter " " (space)
s=map(len,s) maps the words into length of the words in int
s=map(str,s) maps the values into string
s=list(s) converts map object to list
Short version of above function (all in one line)
def countLenWords(s):
return list(map(str,map(len,s.split(" "))))
-- Localise for performance.
local insert = table.insert
local text = 'I am a poor boy straight. I do not need sympathy'
local function word_lengths (text)
local lengths = {}
for word in text:gmatch '[%l%u]+' do
insert (lengths, word:len())
end
return lengths
end
print ('{' .. table.concat (word_lengths (text), ', ') .. '}')
gmatch returns an iterator over matches of a pattern in a string.
[%l%u]+ is a Lua regular expression (see http://lua-users.org/wiki/PatternsTutorial) matching at least one lowercase or uppercase letter:
[] is a character class: a set of characters. It matches anything inside brackets, e.g. [ab] will match both a and b,
%l is any lowercase Latin letter,
%u is any uppercase Latin letter,
+ means one or more repeats.
Therefore, text:gmatch '[%l%u]+' will return an iterator that will produce words, consisting of Latin letters, one by one, until text is over. This iterator is used in generic for (see https://www.lua.org/pil/4.3.5.html); and on any iteration word will contain a full match of the regular expression.

How can I loop through nested tables in Lua when the nested tables are mixed in with other data types?

I'm trying loop though a very large table in Lua that consists of mixed data types many nested tables. I want to print the entire data table to the console, but I'm having trouble with nested loops. When I do a nested loop to print the next level deep Key Value pairs I get this error bad argument #1 to 'pairs' (table expected, got number) because not all values are tables.
I've tried adding a if type(value) == table then before the nested loop but it never triggers, because type(value) returns userdata whether they are ints, strings or tables.
EDIT: I was wrong, only tables are returned as type userdata
My table looks something like this but hundreds of pairs and can be several nested tables. I have a great built in method printall() with the tool I'm using for this but it only works on the first nested table. I don't have any control over what this table looks like, I'm just playing with a game's data, any help is appreciated.
myTable = {
key1 = { value1 = "string" },
key2 = int,
key3 = { -- printall() will print all these two as key value pairs
subKey1 = int,
subKey2 = int
},
key4 = {
innerKey1 = { -- printall() returns something like : innerKey1 = <int32_t[]: 0x13e9dcb98>
nestedValue1 = "string",
nestedValue2 = "string"
},
innerKey2 = { -- printall() returns something like : innerKey2 = <vector<int32_t>[41]: 0x13e9dcbc8>
nestedValue3 = int,
nestedValue4 = int
}
},
keyN = "string"
}
My loop
for key, value in pairs(myTable) do
print(key)
printall(value)
for k,v in pairs(value) do
print(k)
printall(v)
end
end
print("====")
end
ANSWER : Here is my final version of the function that fixed this, it's slightly modified from the answer Nifim gave to catch edge cases that were breaking it.
function printFullObjectTree(t, tabs)
local nesting = ""
for i = 0, tabs, 1 do
nesting = nesting .. "\t"
end
for k, v in pairs(t) do
if type(v) == "userdata" then -- all tables in this object are the type `userdata`
print(nesting .. k .. " = {")
printFullObjectTree(v, tabs + 1)
print(nesting .. "}")
elseif v == nil then
print(nesting .. k .. " = nil")
elseif type(v) == "boolean" then
print(nesting .. k .. " = " .. string.format("%s", v))
else
print(nesting .. k .. " = " .. v)
end
end
end
type(value) returns a string representing the type of value
More information on that Here:
lua-users.org/wiki/TypeIntrospection
Additionally your example table has int as some of the values for some keys, as this would be nil those keys are essentially not part of the table for my below example i will change each instance of int to a number value.
It would also make sense to recurse if you hit a table rather than making a unknown number of nested loops.
here is an example of working printAll
myTable = {
key1 = { value1 = "string" },
key2 = 2,
key3 = { -- printall() will print all these two as key value pairs
subKey1 = 1,
subKey2 = 2
},
key4 = {
innerKey1 = { -- printall() returns something like : innerKey1 = <int32_t[]: 0x13e9dcb98>
nestedValue1 = "string",
nestedValue2 = "string"
},
innerKey2 = { -- printall() returns something like : innerKey2 = <vector<int32_t>[41]: 0x13e9dcbc8>
nestedValue3 = 3,
nestedValue4 = 4
}
},
keyN = "string"
}
function printAll(t, tabs)
local nesting = ""
for i = 0, tabs, 1 do
nesting = nesting .. "\t"
end
for k, v in pairs(t) do
if type(v) == "table" then
print(nesting .. k .. " = {")
printAll(v, tabs + 1)
print(nesting .. "}")
else
print(nesting .. k .. " = " .. v)
end
end
end
print("myTable = {")
printAll(myTable, 0)
print("}")

Use variables inside a string in lua

In javascript we can do the following:
var someString = `some ${myVar} string`
I have the following lua code, myVar is a number that needs to be in the square brackets:
splash:evaljs('document.querySelectorAll("a[title*=further]")[myVar]')
Function that fits you description is string.format:
splash:evaljs(string.format('document.querySelectorAll("a[title*=further]")[%s]', myVar))
It is not as verbose as ${}. It is more of a good old (and hated) sprintf.
I have written an emulation of Python's f'' strings, which is a set of functions you can hide inside a require file. So, if you like Python's f'' strings, this may be what you're looking for.
(If anyone finds errors, please notify.)
It's quite big compared to the other solution, but if you hide the bulk in a library, then its use is more compact and readable, IMO.
With this library you can do the following, for example:
require 'f_strings'
a = 12345
print(f'Number: {a}, formatted with two decimals: {a::%.2f}')
-- Number: 12345, formatted with two decimals: 12345.00
Note the use of Lua string.format formatting codes, and the use of double colon (instead of Python's single colon) for format specifiers because of Lua's use of colon for methods.
I have extracted only the relevant functions from a larger library. Although some optimizations may be possible for this specific use case, I leave them unchanged as they are general purpose and may also be useful for other purposes.
And here's the required library (placed somewhere in your Lua libraries folder):
-- f_strings.lua ---
unpack = table.unpack or unpack
--------------------------------------------------------------------------------
-- Escape special pattern characters in string to be treated as simple characters
--------------------------------------------------------------------------------
local
function escape_magic(s)
local MAGIC_CHARS_SET = '[()%%.[^$%]*+%-?]'
if s == nil then return end
return (s:gsub(MAGIC_CHARS_SET,'%%%1'))
end
--------------------------------------------------------------------------------
-- Returns iterator to split string on given delimiter (multi-space by default)
--------------------------------------------------------------------------------
function string:gsplit(delimiter)
if delimiter == nil then return self:gmatch '%S+' end --default delimiter is any number of spaces
if delimiter == '' then return self:gmatch '.' end
if type(delimiter) == 'number' then --break string in equal-size chunks
local index = 1
local ans
return function()
ans = self:sub(index,index+delimiter-1)
if ans ~= '' then
index = index + delimiter
return ans
end
end
end
if self:sub(-#delimiter) ~= delimiter then self = self .. delimiter end
return self:gmatch('(.-)'..escape_magic(delimiter))
end
--------------------------------------------------------------------------------
-- Split a string on the given delimiter (comma by default)
--------------------------------------------------------------------------------
function string:split(delimiter,tabled)
tabled = tabled or false --default is unpacked
local ans = {}
for item in self:gsplit(delimiter) do
ans[#ans+1] = item
end
if tabled then return ans end
return unpack(ans)
end
--------------------------------------------------------------------------------
function copy(t) --returns a simple (shallow) copy of the table
if type(t) == 'table' then
local ans = {}
for k,v in next,t do ans[ k ] = v end
return ans
end
return t
end
--------------------------------------------------------------------------------
function eval(expr,vars)
--evaluate a string expression with optional variables
if expr == nil then return end
vars = vars or {}
assert(type(expr) == 'string','String expected as 1st arg')
assert(type(vars) == 'table','Variable table expected as 2nd arg')
local env = {abs=math.abs,acos=math.acos,asin=math.asin,atan=math.atan,
atan2=math.atan2,ceil=math.ceil,cos=math.cos,cosh=math.cosh,
deg=math.deg,exp=math.exp,floor=math.floor,fmod=math.fmod,
frexp=math.frexp,huge=math.huge,ldexp=math.ldexp,log=math.log,
max=math.max,min=math.min,modf=math.modf,pi=math.pi,pow=math.pow,
rad=math.rad,random=math.random,randomseed=math.randomseed,
sin=math.sin,sinh=math.sinh,sqrt=math.sqrt,tan=math.tan,
tanh=math.tanh}
for name,value in pairs(vars) do env[name] = value end
local a,b = pcall(load('return '..expr,nil,'t',env))
if a == false then return nil,b else return b end
end
--------------------------------------------------------------------------------
-- f'' formatted strings like those introduced in Python v3.6
-- However, you must use Lua style format modifiers as with string.format()
--------------------------------------------------------------------------------
function f(s)
local env = copy(_ENV) --start with all globals
local i,k,v,fmt = 0
repeat
i = i + 1
k,v = debug.getlocal(2,i) --two levels up (1 level is this repeat block)
if k ~= nil then env[k] = v end
until k == nil
local
function go(s)
local fmt
s,fmt = s:sub(2,-2):split('::')
if s:match '%b{}' then s = (s:gsub('%b{}',go)) end
s = eval(s,env)
if fmt ~= nil then
if fmt:match '%b{}' then fmt = eval(fmt:sub(2,-2),env) end
s = fmt:format(s)
end
return s
end
return (s:gsub('%b{}',go))
end

Lua return table is nil

I'm learning Lua and probably don't have a great grasp on how the language works but I'm trying to create a split function for the string library:
string.split = function(str, delimiter)
-- A function which splits a string by a delimiter
values = {}
currentValue = ""
for i = 1, string.len(str) do
local character = string.sub(str, i, i)
if(character == delimiter) then
table.insert(values,currentValue)
currentValue = ""
else
currentValue = currentValue..character
end
end
-- clean up last item
table.insert(values,currentValue)
return vaules
end
values is not nil if I print it out before the return, but if I call t = string.split("hello world", " "), t will be nil. I'm not quite sure why my table is disappearing
You have a typo in your return statement.
vaules
Instead of values.
vaules is nil of course.
Another advice: make variables local wherever possible.

Resources