lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

Concerning switch in Lua, here's mine from "fixes.lua".

The benefit this gives is use of { } to gather the cases. The default case is named by 'switch()' 2nd param.

Problem with these is runtime performance (since they're often in event loops etc.) and also that it just Does Not Feel Normal, in Lua. I've ended up not using this, but it might be of interest to some. Not that I'm pleased with if-elseif either.


---=== Switch/case construct ===---
-- Switch and Lua:
-- AK/9-Apr-05: Modified so that 'case' keyword is not required
-- (should also be faster due to less table creation & direct lookup).
-- AK 21-Jul-06: This would benefit from the "do patch", making "= do" the same as -- "= function()". However, this is not standard Lua 5.1 syntax, so
--               we'll need to use the longer form.
-- Usage:
--      switch( action, "" ) {
--              [DOG_BARK]= function() print "Arf!!!" end,
--              [DOG_BITE]= function() print "Chomp!" end,
--              [DOG_SLEEP]= function() print "Zzzzzz..." end,
--              [""]= function() print "Default!" end
--            }
-- Unlike C's 'switch/case' statement, this construct allows providing return values
-- from the cases ('return' is used for that).
-- The 'key' value is provided for the case functions, in case the same function is
-- used for handling multiple cases.
-- Cases can simply have the value of another case, leading to that (and sharing the
-- function).

-- [value=] switch( key_val [,def_key] ) {
--              [case1_val]= func( key_val ) .. end | caseX_val,
--              ..
--              }
function m.switch( key, def )
    if (key==nil) then error( "nil key in 'switch'", 2 ) end
    local orig_key= key

    -- Returned function has 'key' and 'def' as a closure :))))))
    -- [case_ret_val]= func( cases_tbl )
    return function ( cases )
if type(cases)~="table" then error( "Wrong switch syntax (needs table)", 2 ) end
            while true do
                local v= cases[key]
                if type(v)=="function" then
                    return v(orig_key)
                elseif v~=nil then
                    key= v  -- try again
                elseif def~=nil then
                    key= def    -- try with default
return nil -- no case covered, no default (slip through)
                if cases[key]==nil then
error( "Forward to a non-existing case: '"..key.."'", 2 )

-- self-test
assert( m.switch( 5,2 ) { [2]= function(k) return k*2 end,
                          [3]= function(k) return k*k end } == 10 )

assert( m.switch( 'a' ) { ['a']= 'z',
                          ['b']= function() return 1 end,
                          ['z']= function() return 2 end } == 2 )

assert( m.switch( "nosuch" ) { [1]='aa' } == nil )      -- not an error

--assert.fails( function() m.switch( "nosuch", 2 ) { [1]='aa' } end ) -- an error

steve donovan kirjoitti 7.11.2007 kello 10:37:

Hi guys,

This is obviously a perenial topic, since there's a wiki page dedicated to it:

My feeling is that switch is the wrong model; we should look at
Pascal's case statement as more appropriate inspiration. Here are some
possible forms:

    case (k)
      is 10,11: return 1
      is 12: return 2
      is 13 .. 16: return 3	
      else return 4
        matches '^hell': return 5
        matches '(%d+)%s+(%d+)',result:
            return tonumber(result[1])+tonumber(result[2])
        else return 0

You can provide a number of values after is, and even provide a range
of values. matches is string-specific, and can take an extra parameter
which is filled with the resulting captures. This is implementable
using token filter macros so people can get a feeling for its use in

(See, which has been
semi-officially released  - the downloadable source zip contains an
example implementation. Unfortunately, there is a gotcha; Lua
complains of a malformed number if there is no whitespace around
'...'. Also 'result' has to be global. These are implementation
irritations, not relevant to a design discussion, I think),

This case statement is a little bit of syntactical sugar over a chain
of elseif statements, so its efficiency is the same.  What the
compiler actually sees in the first case is:

     do local __case = k; if false then
     elseif __case == 10 or __case == 11 then return 1
    end; end

But already I've thought of some other possibilities. This feels a
little more elegant.

case (k)
  is 10 or 11: return 1
  is 12: return 2
  is between(13,16): return 3
  else return 4

What do you think?

steve d.