Lua: attempt to perform arithmetic on global 'mapfinishes' (a nil value) - lua

I'm trying to set up something in a game that runs off of Lua and this specific local function is triggered when the player finishes the map. Here is the code:
local function setDrRanks( ply )
local name = SQLStr( ply:Nick() )
local sid = ply:SteamID()
drsql:query( "SELECT MapFinishes from dr_exp WHERE SteamID = '"..sid.."'", function( q, data )
local row = data[1]
if ( row ) then
mapfinishes = row["Mapfinishes"]
end
drsql:query( "REPLACE into dr_exp (`SteamID`, `PlayerName`, `MapFinishes`) VALUES('"..sid.."', "..name..", '"..(mapfinishes+1).."');" );
end )
end
The function is to insert into SQL via a lua function, which it did successfully when ran the first time, as the player was at 0 finishes. Once they hit 1, it refused to do a simple +1 on the mapfinishes value. Which strange is that this seems to work 100% when the player is at 0 finishes, and it will put them at 1, but once they are at 1, it will not add to it any longer. The error received is:
attempt to perform arithmetic on global 'mapfinishes' (a nil value)
Anyone have any ideas? Thanks in advance.

local row = data[1]
if ( row ) then
mapfinishes = row["Mapfinishes"]
end
drsql:query( "REPLACE into dr_exp (`SteamID`, `PlayerName`, `MapFinishes`) VALUES('"..sid.."', "..name..", '"..(mapfinishes+1).."');" )
The issue is in the expression mapfinishes+1 which seems to have gotten executed without mapfinishes getting set. This implies that the if loop above didn't execute because row was nil or false. Remember, in Lua, zero and the empty string are truth values.
Another possibility is that the row["Mapfinishes"] itself was nil so that mapfinishes remains nil.
Usually it's better to have minimal/no global variables. If you're going to use mapfinishes only within this function, it'd be appropriate to declare it local.

Related

How do I get this code to continue to pick a random value from my table? Lua Code

if gdt.LedButton0.ButtonState
then gdt.Lcd0.Text = Random
else gdt.Lcd0.Text = Text
end
Random = ( myTable[ math.random( #myTable ) ] )
I want the code to continue to pick a random value from the table when a button is pressed. Currently, it picks a value the first time it is pressed and sticks with that value until the program is reset
I have tried looping the code, but the problem persists
You forgot to seed() it.
local function SomeFuncThatGetsExecutedOnceAtStartOfProgram()
math.randomseed(os.time())
end
(where math is a local reference to the Math library and os is a local reference to the Operating System library)
Then in your function:
Random = ( myTable[ math.random( #myTable ) ] )
(although, keep in mind # doesn't really return the number of items in a table)
Choose Random before or in your if ... then ... else ... end
Like...
-- Maybe you need to set math.randomseed() first?
-- Then...
-- math.randomseed(math.random(os.time()))
if gdt.LedButton0.ButtonState then
Random = myTable[math.random(#myTable)]
gdt.Lcd0.Text = Random
else
gdt.Lcd0.Text = Text
end

Lua - For loops: saving the value of the control variable

I'm having a hard time understanding the example from the doc(https://www.lua.org/pil/4.3.4.html) and need some clarification.
If you need the value of the control variable after the loop (usually when you break the loop), you must save this value into another variable:
-- find a value in a list
local found = nil
for i=1,a.n do
if a[i] == value then
found = i -- save value of `i'
break
end
end
print(found)
I don't understand the a.n and if a[i] == value then parts. Are they creating a table a={n=5,...} and calling a single value like a.n=5?
I think I need a written explanation of what's occurring in the example, and what is missing, or a complete example. I'm guessing its missing the declaration of table/variables...?
Cause a[i] is calling entries of a={} and I don't understand what 'value' is...? A variable I have to declare first and then set to a specific value...? What value though?
Why am I calling other entries in a table (i.e. a[i]) when I'm defining a.n as the entry I want to be dealing with?
And in this case do I have to define the entry I want the control variable to break on by predefining the number and that's what value is set to...?
That would defeat the point of calling the value of the control variable if I already define what its going to be. I'm very confused. Like I understand if the example was:
local found = nil
local a=7
for i=1,a do
print(i)
found=a
break
end
However print(found) is equal to 7 rather than the last iteration of the incomplete for loop (2 or 1?).
What I was looking for was a way to save whatever number the control variable was on when the loop was interrupted.
So if it was for i=1,5 do... and the last printed iteration was 4, how would I call this value? I'm unsure if the doc is providing that in its example or not.
The complete working example may be the following:
local function find_value_in_list(value, a)
-- find a value in a list and print its index
local found = nil
for i=1, a.n do
if a[i] == value then
found = i -- save value of `i'
break
end
end
print(found)
end
find_value_in_list(33, {n=4, 11, 22, 33, 44}) --> 3
find_value_in_list(42, {n=4, 11, 22, 33, 44}) --> nil

How to check if a table contains some key inside __index?

I want to optimize a Fibonacci function but using table index, and memoization seems a good method (iteration is as good)... However, soon I ran into a problem: I can't decide whether a key is in a table. How can I do that?
local fib = {}
function fib_index(t, k)
if k == 0 or k == 1 then
t[k] = k
else
t[k] = t[k-1] + t[k-2]
end
return t[k]
end
setmetatable(fib, {__index = fib_index})
Your code will run just fine in it's current state. The reason behind that is that already strored values have highter priority than __index metamethod, so if value does exist, it's returned immideately. There are few optimisations which can make your code look better:
local fib = setmetatable({1, 1}, {__index = function(t,k)
assert(k > 0, 'Invalid fib index') -- Most basic check
local res = t[k-1] + t[k-2]
t[k] = res
return res
end})
Here I remove function declaration at all (if you want to reuse it, consider making your function local with local function instead of function) and made code easier by adding inital values directly into table declaration (no index 0 to keep it lua way, also no zero in results) and utilizing the fact that setmetatable return originally passed table. You can remove assert if you want to, but it's probably good idea to see meaningful error message instead of "stack overflow".
And if you really wanna check does value exist in table (this code does not require this), use rawget:
rawget(fib, 10) == nil
will tell you is 10 already calculated and cached.

Why is this function to add the contents of a table together in Lua returning nothing

I am stuck trying to make the contese of a table (all integers) add together to form one sum. I am working on a project where the end goal is a percentage. I am putting the various quantities and storing them in one table. I want to then add all of those integers in the table together to get a sum. I haven't been able to find anything in the standard Library, so I have been tyring to use this:
function sum(t)
local sum = 0
for k,v in pairs(t) do
sum = sum + v
end
return sum
However, its not giving me anything after return sum.... Any and all help would be greatly appreciated.
A more generic solution to this problem of reducing the contents of a table (in this case by summing the elements) is outlined in this answer (warning: no type checking in code sketch).
If your function is not returning at all, it is probably because you are missing an end statement in the function definition.
If your function is returning zero, it is possible that there is a problem with the table you are passing as an argument. In other words, the parameter t may be nil or an empty table. In that case, the function would return zero, the value to which your local sum is initialized.
If you add print (k,v) in the loop for debugging, you can determine whether the function has anything to add. So I would try:
local function sum ( t ) do
print( "t", t ) -- for debugging: should not be nil
local s = 0
for k,v in pairs( t ) do
print(k,v) --for debugging
s = s + v
end
return s
end
local myTestData = { 1, 2, 4, 9 }
print( sum( myTestData) )
The expected output when running this code is
t table: [some index]
1 1
2 2
3 4
4 9
16
Notice that I've changed the variable name inside the function from sum to s. It's preferable not to use the function name sum as the variable holding the sum in the function definition. The local sum in the function overrides the global one, so for example, you couldn't call sum() recursively (i.e. call sum() in the definition of sum()).

Lua: When and how to write tables to _G

I am learning Lua from a book, and I am NOT a programmer. I am trying to save a table of data to a file using the following functions (that were copied directly from the book), but the function is getting an error when trying to get a string from _G[resTable]. Why?
function readFromFile(filename,resTable)
local hfile = io.open(filename)
if hfile == nil then return end
local results = {} -why is this table here?
local a = 1
for line in hfile:lines() do-- debug shows this loop doesn't run (no lines in hfile?)
_G[resTable[a]] = line
a = a + 1
end
end
function writeToFile(filename, resTable)
local hfile = io.open(filename, "w")
if hfile == nil then return end
local i
for i=1, #resTable do
hfile:write(_G[resTable[i]])--bad argument #1 to 'write' (string expected, got nil)
end
end
'writeToFile" gets an error when trying to :write to _G[resTable[i]]. In the two previous functions listed here, I don't understand why they are referencing _G[resTable[i]] since I don't see any code that is writing to _G.
So here is the order of execution:
local aryTable = {
"Score",
"Lives",
"Health",
}
readFromFile("datafile", aryTable)
writeToFile("datafile", aryTable)
and I get an error:
bad argument #1 to 'write' (string expected, got nil)
stack traceback:
[C]: in function 'write'
test.lua:45: in function 'writeToFile'
test.lua:82: in main chunk
Apparently the author has implemented a way of saving a list of global variables to file and restore them.
The function writeToFile expects a filename and a list of global variables names (resTable). Then it opens a the filename for writing and iterates over the provided names:
for i=1, #resTable do
hfile:write(_G[resTable[i]])
end
in this loop resTable[i] is the i-th name and _G[resTable[i]] is the corresponding value, taken from the table _G, which stores all the globals. If a global with that name is not defined, _G[resTable[i]] will return nil, which is the cause of the failure you experienced. Thus you must provide a resTable that is filled with names of existing globals to avoid this error.
Apart from this, the serialization strategy of the author is really naive, since it handles only variables with string values. In fact by saving the variables to file like that the type information is lost, thus a variable having the value "100" (a string) and another with value 100 (a number) will be stored the same on disk.
The problem is evident analyzing the readFromFile function. After opening the file for reading, it scans it line by line, creating a new variable for each name mentioned in its resTable list:
local a = 1
for line in hfile:lines() do
_G[resTable[a]] = line
a = a + 1
end
the problem is manyfold:
the loop variable line will always have a string value, thus the recreated globals will be all strings, even if they were numbers originally;
it assumes that the variables are recreated in the same order, thus you must provide the same names in resTable you used when you saved the file;
it assumes that the values are stored one per line, but this is a false assumption, since the writeToFile function doesn't write a newline character after each value;
Moreover that local results = {} is useless and in both functions the file handle hfile is not closed. This latter is very bad practice: it could waste system resources and if your script fails part of the supposedly written data could never make its way to disk, since it may be still stuck in some buffer. File handles are automatically closed when the script ends, but only if it ends in a sane way.
Unless you did some error in pasting the code or omitted significant parts of it or the book is building some example incrementally, I dare say it is fairly crappy.
If you want a quick and dirty way to save and retrieve some globals you could use this:
function writeToFile( filename, resTable )
local hfile = io.open(filename, "w")
if hfile == nil then return end
for _, name in ipairs( resTable ) do
local value = _G[name]
if value ~= nil then
hfile:write( name, " = ")
local vtype = type( value )
if vtype == 'string' then
hfile:write( string.format( "%q", value ) )
elseif vtype == 'number' or vtype == 'boolean' then
hfile:write( tostring( value ) )
else
-- do nothing - unsupported type
end
hfile:write( "\n" )
end
end
hfile:close()
end
readFromFile = dofile
It saves the globals as a Lua script and reads them back by executing the script using Lua dofile function. Its main limitation is that it can only save strings, booleans an numbers, but usually this is enough while learning.
You can test it with the following statements:
a = 10
b = "20"
c = "hello"
d = true
print( a, b, c, d )
writeToFile( "datafile", { "a", "b", "c", "d" } )
a, b, c, d = nil
print( a, b, c, d )
readFromFile( "datafile" )
print( a, b, c, d )
If you need more advanced serialization techniques you can refer to Lua WIKI page on table serialization.
Those aren't generalized "read/write any table from/to any file" functions. They apparently expect the name of a global table as an argument, not a [reference to a local] table itself. They look like the kind of one-off solution to a very specific problem that tends to show up in books. :-)
Your functions shouldn't be doing anything with _G. I don't have an API reference handy, but the read loop should be doing something like
resTable[a] = line
and the write loop would be doing
hfile:write(resTable[i])
Throw out that local "results" table too. :-)
This code reads and writes data from a file into global variables whose names are specified in aryTable. Since your file is empty, readFromFile does not actually set the variable values. And then writeToFile fails when trying to get the variable values, because they haven't been set.
Try putting data in the file so that the variables do get set, or set the variable values yourself before writing them to the file (e.g. Score = 10, etc.)

Resources