Cycling through different file location options - lua

I'm essentially trying to create a function which tests the first location I give, in the form:
myComputer.referenceLookup("/address/x/text")
and return the string in that location if it is not NULL or "None" or "" (empty).
If not, I want it to test the next possible location:
myComputer.referenceLookup("/address/1/x/text")
Otherwise, I would like it to return an empty string ("").
I've tried looking in the Lua Manual to no avail as well as testing different forms in repl.it, but unfortunately, I can't replicate a similar example as I usually do when testing.
function firstLine(x)
if myComputer.referenceLookup("/Address/ .. (x) .. /text") != NULL or "None" or "" then
return myComputer.referenceLookup("/Address/ .. (x) .. /text")
elseif myComputer.referenceLookup("/Address/1/ .. (x) .. /text") != NULL or "None" or "" then
return myComputer.referenceLookup("/Address/1/ .. (x) .. /text")
else
return ""
end
end
myComputer.out.firstHouseNumber = firstLine(housenumber)
It's worth noting that the usual way I would reference the fact is as follows:
myComputer.out.firstHouseNumber= myComputer.referenceLookup("/Address/housenumber/text")
or
myComputer.out.firstHouseNumber= myComputer.referenceLookup("/Address/1/housenumber/text")
The platform I'm using doesn't throw errors, it just will return blank instead of running the lua script so I am unable to debug (hence usually using repl.it).
I know this makes it a bit of an abstract question, but if anyone knows how I can do what I am describing, it would be very much appreciated.

Assumptions
Looking at your answer, I will assume that
myComputer.referenceLookup is defined somewhere else and works as intended (and not part of this question)
NULL is also defined somewhere else and represents some sort of nil-value
Answer
The line
if myComputer.referenceLookup("/Address/ .. (x) .. /text") != NULL or "None" or "" then
doesn't work, because the or operator doesn't work that way.
How Lua interprets it is
if (myComputer.referenceLookup("/Address/ .. (x) .. /text") != NULL) or "None" or ""
and since "None" is a String value and thus considered truthy, the if condition will always evaluate to true, so it will always return the first location. Also, there is no != operator in Lua; it's ~= instead.
As for a solution, you essentially need three comparisons like this:
if myComputer.referenceLookup("/Address/" .. x .. "/text") ~= NULL
and myComputer.referenceLookup("/Address/" .. x .. "/text") ~= "None"
and myComputer.referenceLookup("/Address/" .. x .. "/text") ~= "" then
Obviously calling the function three times is a bad idea, both because of performance and because it may have side effects, so it's better to save it into a variable first like so:
local result = myComputer.referenceLookup("/Address/" .. (x) .. "/text")
if result ~= NULL and result ~= "None" and result ~= "" then
return result
end
Extra
If you want to make your program easier to extend, you can also use string.format to build the locations from templates. Say you have a table containing all your locations like this:
local locations = {
"/Address/%s/text";
"/Address/1/%s/text";
}
Then you can iterate through the entries using ipairs and build each location using string.format:
for index, template in ipairs(locations) do
local result = myComputer.referenceLookup(template:format(x))
if result ~= NULL and result ~= "None" and result ~= "" then
return result
end
end
Note that you can write string.format(template, x) as template:format(x) as long as template is a string. (further reading)

Related

Is there a way to make a more compact answer check

I'm very new to coding and right now my code is really bulky and I want to know if there is a way to make a more compact function to check answers, right now I just have if then statements copied and pasted over and over with the variable capitalized and spelled a different way every time, for example, for no I have if then statements for N,n,no,NO,No,nO.
local men = io.read()
if men == "N" then
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
return
end
if men == "NO" then
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
return
end
if men == "no" then
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
return
end
if men == "No" then
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
return
end
if men == "n" then
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
return
end
You can use patterns. They are very similar to regular expressions which are used across most programming languages. Here I test the answer string to see if it matches the pattern you're looking for. Here's an explanation of pattern:
^ - match the start of the string, don't allow any characters before this. If you don't include this then it could find 'no' later in the string, i.e. abcdNO
[nN] - the [] let you include a list of acceptable characters, so here the first character needs to be n or N
[oO]? - the next character has to be o or O, but the ? means it is optional, it can occur 0 or 1 times.
$ matches the end of the string, so it will not match 'NOabcd` because there can't be anything after your pattern.
In all that means the string has to start with 'n' or 'N', possibly have a single 'o' or 'O', and have nothing else after that.
string.find(string, pattern) will see if string is matched by pattern and return the position it was found in the string, or nil if not found.
local answer = 'No'
local pattern = '^[nN][oO]?$'
if (string.find(answer, pattern) ~= nil) then
print('found!')
else
print('not found!')
end
You can use a set then you check if the input is a member of the set. a simple set in Lua can be defined like so:
local no = {
N = true,
n = true,
no = true,
NO = true,
No = true,
nO = true
}
and you use it just by indexing it like any table:
local men = io.read()
if no[men] then
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
return
end
a nil in lua will be treated as a false in this context, and you will get nil from any value that is not a key in the set
I'm adding another answer to offer a simpler change. You should generally try to find a way to avoid duplicate code. The same 'print' statement is repeated several times.
One thing you can do is use the or operator and combine all your tests into one expression.
local men = io.read()
if men == "N" or men == "NO" or men == "no" or men == "No" or men == "n" then
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
return
end
That is the best method for the setup you have, but if you don't have a simple condition like this and want to reuse the code you could create a function with the duplicated code and call it instead. Say if you wanted to do something ELSE as well depending on certain values:
function badEnding()
print(" You decide that you're fine with getting pushed around for your whole life, so you continue like that until you are old and die. THE END")
-- NOTE: return is not required, the default return value will be nil
end
local men = io.read()
if men == "N" then return badEnding() end
if men == "NO" then
print("Hey, no need to shout!")
return badEnding()
end
if men == "no" then return badEnding() end
if men == "No" then return badEnding() end
if men == "n" then return badEnding() end
You can put your options into a list and than ask through a for-loop if your input is equal to a statement in your list. If not there isn't a mistake.
For example if you have list like:
l_words = ["N","n","no","NO","N0","nO"]
inp = input("Your input: ")
for i in l_words:
if inp != i:
print("true input")
else:
print("false input")

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.

Multiple Messages & Issues Searching Lua Tables

I am attempting to make a Lua script for an online community I am a part of, I am having a problem when I attempt to search through a table array I believe. It doesn't detect the results I want.
The way it is supposed to work is that when someone types /gps [streetname] it will search the table at the top, detect the matching streetname & the coordinates and then set a waypoint to that relevant position.
At the moment it works when there is just one entry in the table, but when I put more, it will provide the error message for any non-matching streets & then the waypoint set message for the matching streets. I've Googled and don't appear to be able to find anything to help.
Any help would be appreciated.
waypoint = {
{404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
{360.85, -956.46, 'atleestreet', 'Atlee Street'},
{500.48, -956.80, 'littlebighornavenue', 'Little Bighorn Avenue'},
}
RegisterCommand('gps', function(source, args, rawCommand)
for k,v in pairs(waypoint) do
x, y, streetname, displayname = table.unpack(v)
results = ""
if args[1] == nil then
if IsWaypointActive() then
SetWaypointOff()
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7Your GPS system has been reset.')
return end
elseif args[2] == nil and args[3] == nil then
results = args[1]
elseif args[2] ~= nil and args[3] == nil then
results = args[1] .. args[2]
else
results = args[1] .. args[2] .. args[3]
end
results = string.lower(results) -- This convertes the args into lower case
end
-- This locates the streetname and sets a waypoint to it for the player
if string.find(streetname, results) then
SetNewWaypoint(x, y)
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7Your waypoint to ^1' .. displayname .. '^r^7 has been set.')
else
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7There has been an error with your street name, please try again.')
end
end)
TriggerEvent('chat:addSuggestion', '/gps', 'This creates a waypoint to your designated street. ^*USE: /gps [streetname]')
To be honest, your code makes little to no sense, and it's probably because you're not using all the nice stuff Lua has to offer.
{404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
You're storing redundant data there. The third value is really just the fourth one with spaces removed and all lowercase.
'sinnerstreet' == ('Sinner Street'):gsub("[^%l]", ""):lower()
In english: take "Sinner Street", globally (meaning in the entire string) substitute everything that is not a lowercase (%l) letter with nothing (""), then make the result of that lowercase. What you get is "sinnerstreet".
x, y, streetname, displayname = table.unpack(v)
Using globals there, that's not good. Globals are the devil. Don't use them.
Then, a few lines further down:
SetNewWaypoint(x, y)
Think about it for a moment. You set x and y in each iteration of your for loop. After the loop is done, they always contain the coordinates of the last waypoint you iterated over. I doubt that's what you want. Use local; it forces you to think what you want the scope of your variables to be, which will help you spot this kind of problem.
elseif args[2] ~= nil and args[3] == nil then
results = args[1] .. args[2]
Unless you specifically want to limit it to 3 arguments, which I doubt, you can also use table.concat to concatenate all the values in a sequence (read: array)
results = string.lower( table.concat(args) )
The thing that puzzles me is why you do this in a loop. For every waypoint, you set result to the same value, which is all the arguments concatenated and converted to lower case.
now what though? You check if result (what the user searched for) contains streetname, which, as we have previously found out, contains the name of the last waypoint in the list.
Using tables for searching
Lua has tables, one of if not the most powerful general-purpose data structure in programming.
local map = {}
for _,waypoint in ipairs(waypoints) do
map[waypoint[3]:lower()] = waypoint
end
This will get you something that looks about like this:
local map = {
sinnerstreet = {404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
atleestreet = {360.85, -956.46, 'atleestreet', 'Atlee Street'},
littlebighornavenue ={500.48, -956.80, 'littlebighornavenue', 'Little Bighorn Avenue'},
}
and if you want to know if a street exists, you can just do this:
if map['atleestreet'] then
print(map.atleestreet[4])
end
if treats everything that isn't false or nil as truthy, so you can just write `map['atleestreet'] in the condition
my_table['text'] can be written as my_table.text
Looking up string indices in a table is pretty fast because of how it's implemented.
Conclusion
Try thinking your code through. If necessary, go through it line by line, writing down what values the variables hold in each moment. If you've been at it for a while, get some rest first or do something else for a while.
Then set your variables to local wherever possible (read: everywhere), figure out what needs to be inside and outside the loop and try again.
Remarks
Instead of if something == nil you can just write if not something, and if something ~= nil just if something
Apologies
Sorry for the long wall of text and using spaces inside brackets, but I wanted things to be specially easy to understand.

Lua double results showing

So my lua script is showing double results:
It should only show one of each type of fluid.
This is the part of the script :
function firstToUpper(str)
return (str:gsub("^%l", string.upper))
end
function dispTanks()
mon.setCursorPos(offsetPos, 1)
mon2.setCursorPos(offsetPos,1)
for i=1, #machines do
-- RC Tanks --------------------------------------------
if string.find(machines[i], "rcirontankvalvetile")
or string.find(machines[i], "rcsteeltankvalvetile") then
if peripheral.isPresent(machines[i]) then
periph = peripheral.wrap(machines[i])
fluidRaw, fluidName, fluidAmount, fluidCapacity, fluidID = marik.getTank(periph)
if fluidName == nil then
-- does not display empty tanks
elseif fluidName ~= nil then
mon2.setTextColor(tc)
x,y = mon2.getCursorPos()
mon2.setCursorPos(offsetPos, (y+1))
mon2.clearLine()
-- marik.cString(offsetPos,(y+1), tc, right, ",")
nameFL = firstToUpper(marik.comma(fluidName):match("[^.]*"))
mon2.write("Tank (" .. nameFL .. ") : " .. marik.getBuckets(fluidAmount) .. " buckets")
end
end
end
end
end
I though it was not ending the showing with a "," "." or a ")" but that doesn't seem to be the case. How can i fix this?
Pastebin edit
This are the 2 complete codes:
The main program : http://pastebin.com/ejVPwW4Q
The api : http://pastebin.com/uycrzMTy
After taking a look at this i would suggest taking a look into what your table looks like because the code posted above does not seem to have anything wrong with it, BUT if your table some how duplicated the machines then it would certainly print it out twice, that's where i would start to look.
Edit - and by table i mean the "array" machines
Code to debug the table "array" put this before the section of code you placed on your question..
for k, v in pairs(machines) do
print(tostring(k)..": "..tostring(v))
end

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