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")
Related
I'm new to this platform and I'm still learning to
program in Lua, so, if any newbie errors appear, forgive me.
The following code is from one of the functions in my project that reads the insert
of the user and validates whether or not it is a data of type "Number". If,
the loop will be broken and the function will return the user input, otherwise, the
program will ask the user to enter the data again:
function bin.readnum(text)
local insertion
if text == nil then text = "Text: " end
while (insertion == nil) do
insertion = nil
print(text)
insertion = io.read("number")
if insertion ~= nil then break end
end
return insertion
end
But, if the user enters a wrong data (string) the function prints the text
madly instead of asking the user to re-enter the data.
When io.read fails to parse the data it got into a number, it doesn't discard it, but instead leaves it in the buffer for the next call to it. That means that in your code, instead of letting the user enter something else, it'll just keep trying to parse the same non-number forever. To fix it, in your if insertion ~= nil then block, do io.read() right before break, to read and discard the whole invalid line.
In addition to what Joseph Sible said:
io.read("number") is wrong: 5.1 docs demand "*n" and 5.4 docs demand just "n" for reading numbers. It probably works nevertheless due to Lua just searching for the chars in the string.
I recommend just replacing insertion = io.read("number") withinsertion = tonumber(assert(io.read(), "EOF")) - this will read a line and try to parse it as a number; the assert gracefully deals with nil being returned by io.read for EOF.
You don't need to set insertion to nil, the later assignment will do that already if what was read is not a valid number.
Style: Consider replacing your explicit nil checks with truthiness checks and removing the parentheses around the while-condition. You don't need a break, you can immediately return the read number; finally, you can even replace the entire loop with tail recursion.
All in all I'd rewrite it as follows:
function bin.readnum(text)
print(text or "Text: ")
local num = tonumber(assert(io.read(), "EOF"))
if num then return num end
return bin.readnum(text)
end
or alternatively using a repeat-until loop:
function bin.readnum(text)
local num
repeat
print(text or "Text: ")
num = tonumber(assert(io.read(), "EOF"))
until num
return num
end
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)
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.
I need this for a game server using Lua..
I would like to be able to save all combinations of a name
into a string that can then be used with:
if exists (string)
example:
ABC_-123
aBC_-123
AbC_-123
ABc_-123
abC_-123
etc
in the game only numbers, letters and _ - . can be used as names.
(A_B-C, A-B.C, AB_8 ... etc)
I understand the logic I just don't know how to code it:D
0-Lower
1-Upper
then
000
001
etc
You can use recursive generator. The first parameter contains left part of the string generated so far, and the second parameter is the remaining right part of the original string.
function combinations(s1, s2)
if s2:len() > 0 then
local c = s2:sub(1, 1)
local l = c:lower()
local u = c:upper()
if l == u then
combinations(s1 .. c, s2:sub(2))
else
combinations(s1 .. l, s2:sub(2))
combinations(s1 .. u, s2:sub(2))
end
else
print(s1)
end
end
So the function is called in this way.
combinations("", "ABC_-123")
You only have to store intermediate results instead of printing them.
If you are interested only in the exists function then you don't need all combinations.
local stored_string = "ABC_-123"
function exists(tested_string)
return stored_string:lower() == tested_string:lower()
end
You simply compare the stored string and the tested string in case-insensitive way.
It can be easily tested:
assert(exists("abC_-123"))
assert(not exists("abd_-123"))
How to do this?
There's native function in Lua to generate all permutations of a string, but here are a few things that may prove useful.
Substrings
Probably the simplest solution, but also the least flexible. Rather than combinations, you can check if a substring exists within a given string.
if str:find(substr) then
--code
end
If this solves your problem, I highly reccomend it.
Get all permutations
A more expensive, but still a working solution. This accomplishes nearly exactly what you asked.
function GetScrambles(str, tab2)
local tab = {}
for i = 1,#str do
table.insert(tab, str:sub(i, i))
end
local tab2 = tab2 or {}
local scrambles = {}
for i = 0, Count(tab)-1 do
local permutation = ""
local a = Count(tab)
for j = 1, #tab do
tab2[j] = tab[j]
end
for j = #tab, 1, -1 do
a = a / j
b = math.floor((i/a)%j) + 1
permutation = permutation .. tab2[b]
tab2[b] = tab2[j]
end
table.insert(scrambles, permutation)
end
return scrambles
end
What you asked
Basically this would be exactly what you originally asked for. It's the same as the above code, except with every substring of the string.
function GetAllSubstrings(str)
local substrings = {}
for i = 1,#str do
for ii = i,#str do
substrings[#substrings+1]=str:sub(ii)
end
end
return substrings
end
Capitals
You'd basically have to, with every permutation, make every possible combination of capitals with it.
This shouldn't be too difficult, I'm sure you can code it :)
Are you joking?
After this you should probably be wondering. Is all of this really necessary? It seems like a bit much!
The answer to this lies in what you are doing. Do you really need all the combinations of the given characters? I don't think so. You say you need it for case insensitivity in the comments... But did you know you could simply convert it into lower/upper case? It's very simple
local str = "hELlO"
print(str:lower())
print(str:upper())
This is HOW you should store names, otherwise you should leave it case sensitive.
You decide
Now YOU pick what you're going to do. Whichever direction you pick, I wish you the best of luck!
In my lua program, i want to stop and ask user for confirmation before proceeding with an operation. I'm not sure how to stop and wait for user input, how can it be done?
local answer
repeat
io.write("continue with this operation (y/n)? ")
io.flush()
answer=io.read()
until answer=="y" or answer=="n"
Take a look at the io library, which by default has standard-input as the default input file:
http://www.lua.org/pil/21.1.html
I've worked with code like this. I will type this in a way it will work:
io.write("continue with this operation (y/n)?")
answer=io.read()
if answer=="y" then
--(put what you want it to do if you say y here)
elseif answer=="n" then
--(put what you want to happen if you say n)
end
I use:
print("Continue (y/n)?")
re = io.read()
if re == "y" or "Y" then
(Insert stuff here)
elseif re == "n" or "N" then
print("Ok...")
end
try to use folowing code
m=io.read()
if m=="yes" then
(insert functions here)
end
print("Continue (y/n)?")
re = io.read()
if re == "y" or "Y" then
(Insert stuff here)
elseif re == "n" or "N" then
print("Ok...")
end
From the bit of lua that I've done (not a lot), I'm going to say that using both uppercase and lowercase letters is redundant if you use string.sub.
print("Continue? (y/n)")
local re = io.read()
--[[Can you get string.sub from a local var?
If so, this works. I'm unfamiliar with io(game
lua uses GUI elements and keypresses in place of the CLI.]]
if re.sub == "y" then
--do stuff
if re.sub == "n" then
--do other stuff
end
That should work.