I have this piece of code in C++ and i want to know how can i write some codes that replace switch statement in Lua because i face many problems and i need to use this statement.
int choice;
do// loop
{
cout<<"\n >>> The General Menu <<< \n";
cout << endl;
cout<< " press (1) to Add "<<endl;
cout<< " press (2) to Save "<<endl;
cout<< " press (3) to Quit " << endl;
cout<< endl;
cout<< "Enter your choice please (1/2/3): ";
cin>>choice;
switch(choice)
{
case 1:
add();
break;
case 2:
save();
break;
default:
cout<<" The program has been terminated "<<endl;
cout<<" Thank you! \n";
}
} while (choice != 3);
}
The statement has been used inside a do..while loop.
In general, if you want a switch statement in Lua, what you ought to be doing is building a table. For your simple case of choice that could be 1, 2, or fail, a simple if statement with a few conditions is sufficient. For more complex cases, a table of functions should be employed:
local c_tbl =
{
[1] = add,
[2] = save,
}
local func = c_tbl[choice]
if(func) then
func()
else
print " The program has been terminated."
print " Thank you!";
end
You can use lexical scoping to allow the functions in the table to be able to access local variables, just as if the code was written inline.
Try this one (click here to run the script in a Lua compiler), Hope the code is self-explanatory ;-) and
resembles the same pseudo code format..!!
print("enter your choice : ")
mychoice = io.read()
switch = function (choice)
-- accepts both number as well as string
choice = choice and tonumber(choice) or choice -- returns a number if the choic is a number or string.
-- Define your cases
case =
{
[1] = function ( ) -- case 1 :
print("your choice is Number 1 ") -- code block
end, -- break statement
add = function ( ) -- case 'add' :
print("your choice is string add ") -- code block
end, -- break statement
['+'] = function ( ) -- case '+' :
print("your choice is char + ") -- code block
end, -- break statement
default = function ( ) -- default case
print(" your choice is din't match any of those specified cases")
end, -- u cant exclude end hear :-P
}
-- execution section
if case[choice] then
case[choice]()
else
case["default"]()
end
end
-- Now you can use it as a regular function. Tadaaa..!!
switch(mychoice)
Lua:
if choice == 1
then add()
elseif choice == 2
then save()
else print "The program has been terminated\nThank you!"
end
one more version of switcher (without initializing table as variable):
local case=2;
local result=({[1]="case1", [2]="case2", 3="case3"})[case];
print (result); --> case2
While simply creating a table indexed by cases with functions as elements is most probably the fastest approach, there is this solution I've made which IMO has better code readability:
function switch(element)
local Table = {
["Value"] = element,
["DefaultFunction"] = nil,
["Functions"] = {}
}
Table.case = function(testElement, callback)
Table.Functions[testElement] = callback
return Table
end
Table.default = function(callback)
Table.DefaultFunction = callback
return Table
end
Table.process = function()
local Case = Table.Functions[Table.Value]
if Case then
Case()
elseif Table.DefaultFunction then
Table.DefaultFunction()
end
end
return Table
end
Example Use:
switch(Player:GetName())
.case("Kate", function() print("This player's name rhymes with Fate")end)
.case("Tod", function() print("This player's name rhymes with Cod") end)
.default(function() print("This player's name is not Kate or Tod") end)
.process()
I encountered this issue with functions that would take different parameters - something which the other answers don't handle well.
I solved that with anonymous functions.
-- call the relevant execution based on its opcode
local instructions = {
[01] = function () self:perform_add(table.unpack(valargs)) end,
[02] = function () self:perform_multiply(table.unpack(valargs)) end,
[03] = function () self:perform_store_input(outputargs[1]) end,
[04] = function () self:perform_output(valargs[1]) end,
[05] = function () self:perform_jnz(table.unpack(valargs)) end,
[06] = function () self:perform_jz(table.unpack(valargs)) end,
[07] = function () self:perform_less_than(table.unpack(valargs)) end,
[08] = function () self:perform_equals(table.unpack(valargs)) end,
[99] = function () self:perform_exit() end,
}
local instr = instructions[opcode]
if (instr) then
instr()
else
print("No instruction for opcode " .. opcode)
end
The actions I want to take in my different switch cases are all defined as anonymous functions in a table. The keys used (e.g. 08 here) are the values our variable to switch on might assume (opcode here). The default case of the switch statement happens in my else clause. There is no requirement for a break equivalent - but if you want to have one case continue with the next you would have to call it explicitly.
Reply to comment asking for clarification:
You're right that this example is not complete. You can find my usage here when I did adventofcode 2019 day 7. I can try answer your questions but I never touched lua before, and never after. valargs is a table of arguments because different functions here take different numbers of arguments. But that is not necessarily relevant to the question. Basically, I'm just calling functions here.
In my example, self exists because I defined the functions on a local (and did some weird changes as outlined here). The relevant code parts:
-- a "class"
local IntComputer = {}
function IntComputer:perform_exit()
self.program_ended = true
end
function IntComputer:perform_add(a, b, target)
print(" " .. a .. " + " .. b .. " => " .. target)
self:set_value(target, a+b)
end
If you want switch as a function that is callable, you could use something funny with the callback feature:
(The example below is a switch statement based on the variable type, but you could make the table index into whatever you want to test it for. Just change the return statement of the switch function to not test for type(case))
(This is essentially a lazy table lookup much like Python's dictionary feature but each element is a function)
#!/usr/bin/lua
-- Callback switch statement:
local function switch(a, case)
-- Local variable instead of function(a) on every case:
local value = a
-- Cases list:
local switchcase = {}
-- Cases:
switchcase["string"] = function()
return (tostring(value) .. " is a string")
end
switchcase["number"] = function()
return tostring(value .. " is a number")
end
switchcase["boolean"] = function()
return (tostring(avalue) .. " is a boolean")
end
return switchcase[type(case)](a)
end
local value = 5
print(switch(value,value)) --> 5 is a number
local value = "test"
print(switch(value,value)) --> test is a string
local value = true
print(switch(value,value)) --> true is a boolean
I don't know the performance of this code compared to the two answers given above, but using local variables ought to make it quick enough for repeated use. If you make your switch function in the global scope it could become a standard function for your project to be used.
Here's another fun method using loadstring() and a table lookup.
switch = function(cases,args)
if (cases[args] == nil) then return args else return assert(loadstring ('return ' .. cases[args]))() end
end
local case = 2
local result = switch({
[1] = "2^" .. case,
[2] = string.format("2^%i",case),
[3] = tostring(2^case)
},
case
)
print(result) --> 4
This method is somewhat dangerous to use since loadstring() is similar to Python's eval() function.
I found it ugly to write "function(x)" on every case in the examples provided by the Lua wiki. This is a neat way.
The "default" case is the "return args" part of the function.
I use this code :
while true do local tmpswitch1 = exp ; --[[ switch <exp> do ]]
if tmpswitch1 == exp1 then --[[ case <exp1> : ]]
-- do something
break
end ;if tmpswitch1 == exp2 then --[[ case <exp2> : ]]
-- do something
break
end ; --[[ default : ]]
-- do something
break ; end --[[ switch tmpswitch1 ]]
function case(i,d) return function(t) return t[i] or d end end
x='two'
r=case(x) {
one=1,
two=2,
three=3,
}
case(r,function() print "default" end) {
[1]=function() print "one" end,
[2]=function() print "two" end,
[3]=function() print "three" end,
}()
Related
Hello people of stackoverflow, I've made a (very) simple programming language, it kind of looks like minecraft commands. Here is the code
function wait(cmdSleepGetNum)-- you can name this function
--what ever you want, it doesnt matter
local start = os.time()
repeat until os.time() > start + cmdSleepGetNum
end
local input = io.read()
if input == "/help" then
print"you'll find it out"
else if input == "/say" then
print"what do you want to say?"
local cmdSay = io.read()
print(cmdSay)
else if input == "/stop" then
os.exit()
else if input == "/sleep" then
print"how long?"
local cmdSleepGetNum = io.read()
wait(cmdSleepGetNum)
else if input == "/rand" then
local randNum = math.random()
print(randNum)
end
end
end
end
end
Now I know what you are thinking "what is the problem here?" the problem is that I can only execute one command and after that command is finished by the Lua interpreter, I cannot execute any other commands.
for example:
/rand
0.84018771715471 (the /rand command is executed and prints out a random number)
/rand
stdin:1: unexpected symbol near '/' (this happens when i try to execute another command)
If you replace else if with elseif, you won't need so many end's,
You can get rid of if's altogether, is you use a table,
As #Egor Skriptunoff said, you need to create a main loop to run many commands,
I suggest that you add an optional argument to /sleep and /say.
local function wait (cmdSleepGetNum) -- you can name this function
-- whatever you want, it doesn't matter.
local start = os.time ()
repeat until os.time () > start + cmdSleepGetNum
end
local commands = {
help = function ()
print "you'll find it out"
end,
say = function (arg)
if arg == '' then
print 'what do you want to say?'
arg = io.read ()
end
print (arg)
end,
stop = function ()
os.exit ()
end,
sleep = function (arg)
if arg == '' then
print 'how long?'
arg = tonumber (io.read ())
end
wait (tonumber (arg))
end,
rand = function ()
local randNum = math.random ()
print (randNum)
end,
[false] = function () -- fallback.
print 'Unknown command'
end
}
-- Main loop:
while true do
io.write '> '
local key, _, arg = io.read ():match '^%s*/(%S+)(%s*(.*))$' -- you can type /sleep 1, etc. in one line.
local command = key and key ~= '' and commands [key] or commands [false]
command (arg)
end
I'm currently starting work on a text adventure game in Lua--no addons, just pure Lua for my first project. In essence, here is my problem; I'm trying to find out how I can do a "reverse lookup" of a table using one of its variables. Here's an example of what I've tried to do:
print("What are you trying to take?")
bag = {}
gold = {name="Gold",ap=3}
x = io.read("*l")
if x == "Gold" then
table.insert(bag,gold)
print("You took the " .. gold.name .. ".")
end
Obviously, writing a line like this with every single object in the game would be very... exhausting--especially since I think I'll be able to use this solution for not just taking items but movement from room to room using a reverse lookup with each room's (x,y) coordinates. Anyone have any ideas on how to make a more flexible system that can find a table by the player typing in one of its variables? Thanks in advance!
-blockchainporter
This doesn't directly answer your question as you asked it, but I think it would serve the purpose of what you are trying to do. I create a table called 'loot' which can hold many objects, and the player can place any of these in their 'bag' by typing the name.
bag = {}
loot = {
{name="Gold", qty=3},
{name="Axe", qty=1},
}
print("What are you trying to take?")
x = io.read("*l")
i = 1
while loot[i] do
if (x == loot[i].name) then
table.insert(bag, table.remove(loot,i))
else
i = i + 1
end
end
For bonus points, you could check 'bag' to see if the player has some of that item already and then just update the quantity...
while loot[i] do
if (x == loot[i].name) then
j, found = 1, nil
while bag[j] do
if (x == bag[j].name) then
found = true
bag[j].qty = bag[j].qty + loot[i].qty
table.remove(loot,i)
end
j = j + 1
end
if (not found) then
table.insert(bag, table.remove(loot,i))
end
else
i = i + 1
end
end
Again, this isn't a 'reverse lookup' solution like you asked for... but I think it is closer to what you are trying to do by letting a user choose to loot something.
My disclaimer is that I don't use IO functions in my own lua usage, so I have to assume that your x = io.read("*l") is correct.
PS. If you only ever want objects to have a name and qty, and never any other properties (like condition, enchantment, or whatever) then you could also simplify my solution by using key/val pairs:
bag = {}
loot = { ["Gold"] = 3, ["Axe"] = 1 }
print("What are you trying to take?")
x = io.read("*l")
for name, qty in pairs(loot) do
if x == name then
bag.name = (bag.name or 0) + qty
loot.name = nil
end
end
I have a few notes to start before I specifically address your question. (I just want to do this before I forget, so please bear with me!)
I recommend printing to the terminal using stderr instead of stdout--the Lua function print uses the latter. When I am writing a Lua script, I often create a C-style function called eprintf to print formatted output to stderr. I implement it like this:
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
Just be aware that, unlike print, this function does not automatically append a newline character to the output string; to do so, remember to put \n at the end of your fmt string.
Next, it may be useful to define a helper function that calls io.read("*l") to get an entire line of input. In writing some example code to help answer your question, I called my function getline--like the C++ function that has similar behavior--and defined it like this:
local function getline()
local read = tostring(io.read("*l"))
return read
end
If I correctly understand what it is you are trying to do, the player will have an inventory--which you have called bag--and he can put items into it by entering item names into stdin. So, for instance, if the player found a treasure chest with gold, a sword, and a potion in it and he wanted to take the gold, he would type Gold into stdin and it would be placed in his inventory.
Based on what you have so far, it looks like you are using Lua tables to create these items: each table has a name index and another called ap; and, if a player's text input matches an item's name, the player picks that up item.
I would recommend creating an Item class, which you could abstract nicely by placing it in its own script and then loading it as needed with require. This is a very basic Item class module I wrote:
----------------
-- Item class --
----------------
local Item = {__name = "Item"}
Item.__metatable = "metatable"
Item.__index = Item
-- __newindex metamethod.
function Item.__newindex(self, k, v)
local err = string.format(
"type `Item` does not have member `%s`",
tostring(k)
)
return error(err, 2)
end
-- Item constructor
function Item.new(name_in, ap_in)
assert((name_in ~= nil) and (ap_in ~= nil))
local self = {
name = name_in,
ap = ap_in
}
return setmetatable(self, Item)
end
return Item
From there, I wrote a main driver to encapsulate some of the behavior you described in your question. (Yes, I know my Lua code looks more like C.)
#!/usr/bin/lua
-------------
-- Modules --
-------------
local Item = assert(require("Item"))
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
local function printf(fmt, ...)
io.stdout:write(string.format(fmt, ...))
return
end
local function getline()
local read = tostring(io.read("*l"))
return read
end
local function main(argc, argv)
local gold = Item.new("Gold", 3)
printf("gold.name = %s\ngold.ap = %i\n", gold.name, gold.ap)
return 0
end
main(#arg, arg)
Now, as for the reverse search which you described, at this point all you should have to do is check the user's input against an Item's name. Here it is in the main function:
local function main(argc, argv)
local gold = Item.new("Gold", 3)
local bag = {}
eprintf("What are you trying to take? ")
local input = getline()
if (input == gold.name) then
table.insert(bag, gold)
eprintf("You took the %s.\n", gold.name)
else
eprintf("Unrecognized item `%s`.\n", input)
end
return 0
end
I hope this helps!
I am trying to make a quote function in lua, so i can use the arguments as strings but without quotes or access them in some environment. Much like in the second comment on this question
w = print
function test()
local function _ix( _ , k )
w( " _ix \\ " , _ , k )
local v = rawget( _G , k )
w( " <-- " , k )
return k
end
local _ = setmetatable( {} , { __index = _ix } )
local function q( _ ) return _ end
q = setfenv( q , _ )
return q
end
So, when I run it:
q = test()
w( "q( uno )" , q( uno ) )
It doesn't even call the __index metamethod:
---------- Capture Output ----------
q( uno ) nil
> Terminated with exit code 0.
So, what I'm doing wrong?
If I'm understanding correctly, then what you're trying to do doesn't make much sense. uno will be looked up in the environment that q is called in, not with. In your example it's like calling q(nil). The example from the other question works because they're working in the same, global environment.
You can use write a helper function to intercept your current environments nil-lookups, but it must be called preemptively in any environment you want to use these nil-to-string lookups.
local function intercept (tab)
setfenv(2, setmetatable(tab or {}, {
__index = function (_, key)
return key
end
}))
end
And you'll need an environment cloning function, unless you want to manually create your sandboxes every time, else you'll probably mess up parent environments (e.g., _G). You could move this logic inside of intercept for a cleaner function call, but with less flexibility.
local function clone_current_env ()
local env = {}
for key, value in pairs(getfenv(2)) do
env[key] = value
end
return env
end
Using them together, you can cause nil lookups in whichever environment you're in to become strings.
intercept(clone_current_env())
print(type(foo), type(bar)) --> string string
This is some ugly metaprogramming, and I don't really know why you'd want to write code like this, except as a proof of concept.
A full example.
DEMO
local function clone (tab)
local new = {}
for key, value in pairs(tab) do
new[key] = value
end
return new
end
local function enable_nil_strings ()
setfenv(2, setmetatable(clone(getfenv(2)), {
__index = function (env, key)
return key
end
}))
end
local function disable_nil_strings()
setmetatable(getfenv(2), nil)
end
-----------------------------------------------------
print(type(foo), type(bar)) --> nil nil
enable_nil_strings()
print(type(foo), type(bar)) --> string string
disable_nil_strings()
print(type(foo), type(bar)) --> nil nil
Finally, arguably the best way to implement this would be to simply wrap around an execution context:
local function with_nil_strings (context, ...)
local env = {}
for key, value in pairs(getfenv(2)) do
env[key] = value
end
setfenv(
context,
setmetatable(env, {
__index = function (_, key) return key end
})
)
context(...)
end
print(type(foo)) --> nil
with_nil_strings(function ()
print(type(foo)) --> string
end)
print(type(foo)) --> nil
Hope someone can make sense of what I'm attempting to figure out, Just don't seem to understand Lua enough to achieve this.
--[[
tbl.a.test("moo") returns "Table A moo appears"
tbl.b.test("moo") returns "moo appears"
]]
tbl = {
a = { ID = "Table A" },
b = {
test = function(...) print(... .. " appears") end,
},
}
tbl.a__index = function(self, ...) tbl.b[self](tbl.a.ID .. ...) end
What I'm attempting to do is I could create several tables a, c, d, e and not have to copy test to each one. When tbl.a.test, tbl.c.test, tbl.d.test is used, It'll retrieve the tbl.a.ID var, then call tbl.b.test(ID, "moo")
So far all I'm finding out is it's not able to find .test on anything other than tbl.b
** EDIT **
Thank's to support so far the code is now;
tbl = {
a = { ID = "Table A " },
b = { test = function(...) local id, rest = ... print(id .. ": " .. rest) end },
}
setmetatable(tbl.a, {__index=function(self, k, ...) local rest = ... return tbl.b[k](tbl.a.ID, rest) end})
However, the ... is not being progressed for some odd reason :|
You're missing a period between tbl.a and __index.
__index needs to be on a's metatable, not the table itself.
You don't return anything from your __index function
self in the __index function is the table being indexed, not the key (which is the second argument)
This should work:
setmetatable(tbl.a, {__index=function(self, k) return tbl.b[k](tbl.a.ID) end})
--------------------------------------------------------------------------------
-- [Sub]Class creation
--------------------------------------------------------------------------------
function newclass(new_obj,old_obj)
old_obj = old_obj or {} --use passed-in object (if any)
new_obj = new_obj or {}
assert(type(new_obj) == 'table','New Object/Class is not a table')
assert(type(old_obj) == 'table','Old Object/Class is not a table')
old_obj.__index = old_obj --store __index in parent object (optimization)
return setmetatable(new_obj,old_obj) --create 'new_obj' inheriting 'old_obj'
end
--------------------------------------------------------------------------------
prototype = {
test = function(self,s) print('Table ' .. self.id .. ' ' .. s .. ' appears') end
}
tbl = {}
tbl.a = newclass({id = 'A'},prototype)
tbl.b = newclass({id = 'B'},prototype)
tbl.a:test('moo')
tbl.b:test('moo')
The distinction between class and object in Lua is only theoretical. In practice they are implemented exactly the same way.
Anytime you need to do inheritance, you can use my general-purpose newclass() function to either create a new class/object, or inherit from an existing one.
Any common code & data you would like to have passed on should go into the 'prototype' table (whatever you'd like to call it for each case).
Also, you seem to forget to use the method calling syntax (that uses a colon instead of a dot) when calling methods. Without it, the self parameter is not automatically recognized.
I'm trying to use in pairs inside a function but it doesn't work, it just prints the first row (key). This is my code:
set = {1,2,3,4};
unset = {5,6,7,8};
function listen(ftype)
if (ftype == [[~]]) then
for num,str in pairs(set) do
return str;
end
end
if (ftype == [[$]]) then
for num,str in pairs(unset) do
return str;
end
end
end
print(listen([[~]])..[[ =:= ]]..listen([[$]]));
If I do something like this..
for num,str in pairs(unset) do
print(str);
end
It works like a charm. That’s exactly what I want but inside a function.
You can build your own iterator:
function double_pair(t1, t2)
local i = 0
return function() i = i + 1
return t1[i] and t1[i] .. " =:= " .. t2[i]
end
end
Then you can use it like this:
for str in double_pair(set, unset) do
print(str)
end
Output:
1 =:= 5
2 =:= 6
3 =:= 7
4 =:= 8
Note that you don't need semicolons to end your statement, unless the statements are in one line and you want to make them clear. And [[ =:= ]] is usually used to build long multi-line strings, normally we choose to use double quote " =:= " or single quote ' =:= '.
A function can't return multiple times. Putting an unconditional return inside a loop is nonsensical - it will never get to the second iteration of the loop.
You are essentially trying to return multiple values from the function. Lua supports that; you could, for instance, just return 1,2,3,4. For an unknown number of return values, you can build them up in a table and call unpack on it, like so:
function listen(ftype)
local result = {}
local num, str
if (ftype == [[~]]) then
for num,str in pairs(set) do
table.insert(result, str)
end
elseif (ftype == [[$]]) then
for num,str in pairs(unset) do
table.insert(result, str)
end
end
return unpack(result)
end
But since your results are already in a couple tables, it would be silly to reconstruct them that way. You can just unpack the originals:
function listen(ftype)
if (ftype == [[~]]) then
return unpack(set)
elseif (ftype == [[$]]) then
return unpack(unset)
end
end
Great. But when you put the function call into an expression like your print statement, it will only return the first value, which puts you back where you started.
So to print out your pairs, you can't avoid having to either:
1) do some iteration outside the function
or
2) do the actual printing inside the function
The cleanest solution is probably a custom iterator, as suggested by #YuHao.