18

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.

zerocukor287
  • 555
  • 2
  • 8
  • 23
zanaM
  • 189
  • 1
  • 1
  • 6

11 Answers11

27

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks @Nicol this is what i was looking for. I intended to build a table than using `if..then..elseif` statement. It's now working. – zanaM May 28 '16 at 12:36
10

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)
codenio
  • 721
  • 10
  • 17
6

Lua:

if choice == 1
then add()
elseif choice == 2
then save()
else print "The program has been terminated\nThank you!"
end 
Doug Currie
  • 40,708
  • 1
  • 95
  • 119
1

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
hitrpr
  • 11
  • 1
1

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
lucidbrot
  • 5,378
  • 3
  • 39
  • 68
  • This code does not appear to do anything useful. `opcode` is not defined. And if I choose a value for it, Lua reasonably enough complains that `self` is not defined. Also, what is `valargs`? – Faheem Mitha Oct 03 '21 at 19:44
  • @FaheemMitha you're correct that my answer leaves these infos out - they are not relevant to the question. But I've added more information to the end of my answer anyway now. I hope this is sufficient for your curiosity, because I don't remember much of this code. – lucidbrot Oct 03 '21 at 21:13
  • 1
    Hi @lucidbrod. Thank you for taking the trouble to update your answer. Really, I was just looking for self-contained, runnable code that I could execute and modify as necessary. But if you don't remember, don't worry about it. – Faheem Mitha Oct 04 '21 at 07:10
  • 1
    @FaheemMitha [this](https://stackoverflow.com/a/46740840/2550406) answer basically suggest the same thing as mine. Maybe their code is runnable as-is :) – lucidbrot Oct 04 '21 at 08:15
  • 1
    Thanks, lucidbrot. Yes, that answer was indeed runnable and useful. – Faheem Mitha Oct 04 '21 at 10:42
1

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()
Krystilize
  • 11
  • 2
0

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.

Mossarelli
  • 319
  • 2
  • 8
  • I ran some tests on the answers in this question. – Mossarelli Dec 09 '16 at 09:44
  • 1
    I ran some tests on the answers: My local switch function: average: 63 ms in 100000 repetitions. The if, elseif solution: 22 ms The table choice solution: 58 ms. Keywords win the day again. – Mossarelli Dec 09 '16 at 09:51
  • And if you convert local function switch to a global switch, the results is average: 62 ms. – Mossarelli Dec 09 '16 at 09:55
0

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.

Mossarelli
  • 319
  • 2
  • 8
0

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 ]]
0
local myVar = "1"

function switchCase(result,tables)
    if tables[result] then
        if type(tables[result]) == "function" then
            return tables[result] ()
        else
            return tables[result]
        end
    end
    if tables["_"] then
        if type(tables["_"]) == "function" then
            return tables["_"] ()
        else
            return tables["_"]
        end
    end
    return nil
end

print (switchCase(myVar,{
  ["1"] = "result1",
  ["_"] = "result2",
}))
-1
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,
}()