[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Switch/Case statements revisited
- From: Asko Kauppi <askok@...>
- Date: Wed, 7 Nov 2007 15:29:53 +0200
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.
-asko
---=== Switch/case construct ===---
-----
-- Switch and Lua: http://lua-users.org/wiki/SwitchStatement
--
-- 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
else
return nil -- no case covered, no default (slip
through)
end
if cases[key]==nil then
error( "Forward to a non-existing case:
'"..key.."'", 2 )
end
end
end
end
-- 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:
http://lua-users.org/wiki/SwitchStatement
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
endcase
......
case(s)
matches '^hell': return 5
matches '(%d+)%s+(%d+)',result:
return tonumber(result[1])+tonumber(result[2])
else return 0
endcase
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
practice.
(See http://lua-users.org/wiki/LuaMacro, 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
endcase
What do you think?
steve d.