Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Entire forum
➜ MUSHclient
➜ Lua
➜ Select
It is now over 60 days since the last post. This thread is closed.
Refresh page
Posted by
| Metsuro
USA (389 posts) Bio
|
Date
| Tue 03 Jan 2006 01:01 AM (UTC) |
Message
| Uhh i was wondering if lua had a select function like other languages, i think vbs is something like Select case ()
case "1" and case "2" or something... just wondering. |
Everything turns around in the end | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #1 on Tue 03 Jan 2006 02:35 AM (UTC) Amended on Tue 03 Jan 2006 02:52 AM (UTC) by Nick Gammon
|
Message
| Not built-in. Depending on what your reason is for wanting it, various variations on using tables will do the trick. There is a page of such suggestions on the Lua wiki:
http://lua-users.org/wiki/SwitchStatement
My own variant on the ideas presented is this:
function switch (selector, t)
assert (type (t) == "table",
"Table not supplied to switch")
local f = t [selector]
if f then
assert (type (f) == "function", "Not a function")
f ()
else
print "default"
end -- if
end -- switch
-- test
-- example table of selectors and actions
actions = {
[1] = function () print "action 1" end ,
[2] = function () print "action 2" end ,
[3] = function () print "action 3" end,
["a"] = function () print "action a" end ,
}
-- try to switch ...
switch (1, actions)
switch (3, actions)
switch ("a", actions)
switch (99, actions)
Here the "switch" function (you could name it "select" if you want) is used to index into a table which is keyed by the selector to give an action function. The functions shown here are inline functions, however if they were more complex they could be defined separately, like this:
function switch (selector, t)
assert (type (t) == "table", "Table not supplied to switch")
local f = t [selector]
if f then
assert (type (f) == "function", "Not a function")
f ()
else
print "default"
end -- if
end -- switch
-- test
-- example table of selectors and actions
function action_1 ()
print "do something"
print "action 1"
end -- action_1
function action_2_or_3 ()
print "do something"
print "action 2 or 3"
end -- action_2_or_3
function action_a ()
print "this is action 'a'"
end -- action_a
actions = {
[1] = action_1 ,
[2] = action_2_or_3 ,
[3] = action_2_or_3 ,
["a"] = action_a ,
}
-- try to switch ...
switch (1, actions)
switch (3, actions)
switch ("a", actions)
switch (99, actions)
This also illustrates how you can "share" a function between 2 selectors (in this case, 2 and 3) by giving them both the same "action" function.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #2 on Tue 03 Jan 2006 02:38 AM (UTC) Amended on Tue 03 Jan 2006 02:52 AM (UTC) by Nick Gammon
|
Message
| My problem here is that the default behaviour is hard-coded into the switch function. This variation lets you supply the default action to the switch function:
function switch (selector, t, default)
assert (type (t) == "table", "Table not supplied to switch")
local f = t [selector]
if f then
assert (type (f) == "function", "Not a function")
f ()
else
if type (default) == "function" then
default ()
end -- default supplied
end -- if
end -- switch
-- test
-- example table of selectors and actions
function action_1 ()
print "do something"
print "action 1"
end -- action_1
function action_2_or_3 ()
print "do something"
print "action 2 or 3"
end -- action_2_or_3
function action_a ()
print "this is action 'a'"
end -- action_a
actions = {
[1] = action_1 ,
[2] = action_2_or_3 ,
[3] = action_2_or_3 ,
["a"] = action_a ,
}
function default ()
print "default taken here"
end -- default
-- try to switch ...
switch (1, actions, default)
switch (3, actions, default)
switch ("a", actions, default)
switch (99, actions, default)
Alternatively, you might simply want to have a table entry called "default" if you didn't think that would be needed as a selector, and use that if the initial lookup didn't match. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #3 on Tue 03 Jan 2006 02:45 AM (UTC) Amended on Tue 03 Jan 2006 02:53 AM (UTC) by Nick Gammon
|
Message
| Here is an example of having the default action in the table, which seems a bit neater to me. In this case I have chosen a key I think no-one would want in practice, which is 1/0 (divide by zero).
This seems to work OK, as typing:
/print (1/0) --> 1.#INF
Thus, you get a key that is unlikely to be needed in general use.
function switch (selector, t)
assert (type (t) == "table", "Table not supplied to switch")
local f = t [selector]
if f then
assert (type (f) == "function", "Not a function")
f ()
else
f = t [1/0]
if type (f) == "function" then
f ()
end -- default in table
end -- if
end -- switch
-- test
-- example table of selectors and actions
function action_1 ()
print "do something"
print "action 1"
end -- action_1
function action_2_or_3 ()
print "do something"
print "action 2 or 3"
end -- action_2_or_3
function action_a ()
print "this is action 'a'"
end -- action_a
function action_default ()
print "default action"
end -- action_a
actions = {
[1] = action_1 ,
[2] = action_2_or_3 ,
[3] = action_2_or_3 ,
["a"] = action_a ,
[1/0] = action_default
}
-- try to switch ...
switch (1, actions)
switch (3, actions)
switch ("a", actions)
switch (99, actions)
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #4 on Tue 03 Jan 2006 02:47 AM (UTC) Amended on Tue 03 Jan 2006 02:52 AM (UTC) by Nick Gammon
|
Message
| And to make that even neater, define a variable "DEFAULT" so we don't have to remember what our default value is, and change it if, for some reason, 1/0 doesn't work for us:
DEFAULT = 1/0 --> special key for default case
function switch (selector, t)
assert (type (t) == "table", "Table not supplied to switch")
local f = t [selector]
if f then
assert (type (f) == "function", "Not a function")
f ()
else
f = t [DEFAULT]
if type (f) == "function" then
f ()
end -- default in table
end -- if
end -- switch
-- test
-- example table of selectors and actions
function action_1 ()
print "do something"
print "action 1"
end -- action_1
function action_2_or_3 ()
print "do something"
print "action 2 or 3"
end -- action_2_or_3
function action_a ()
print "this is action 'a'"
end -- action_a
function action_default ()
print "default action"
end -- action_a
actions = {
[1] = action_1 ,
[2] = action_2_or_3 ,
[3] = action_2_or_3 ,
["a"] = action_a ,
DEFAULT = action_default
}
-- try to switch ...
switch (1, actions)
switch (3, actions)
switch ("a", actions)
switch (99, actions)
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #5 on Tue 03 Jan 2006 03:00 AM (UTC) Amended on Tue 03 Jan 2006 03:02 AM (UTC) by Nick Gammon
|
Message
| A final version, once we have defined "switch" we can inline the table definition, as an anonymous inline table, to save having to define it separately:
function switch (selector, t, default)
assert (type (t) == "table", "Table not supplied to switch")
local f = t [selector]
if f then
assert (type (f) == "function", "Not a function")
f ()
else
if type (default) == "function" then
default ()
end -- default supplied
end -- if
end -- switch
-- test
function myfunc (sel)
switch (sel,
{
[1] = function () print "action 1" end,
[2] = function () print "action 2" end,
[42] = function () print "action 42" end,
},
-- default:
function () print "default taken" end
) -- end of switch
end -- myfunc
myfunc (1)
myfunc (2)
myfunc (42)
myfunc ("blah")
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Shadowfyr
USA (1,788 posts) Bio
|
Date
| Reply #6 on Tue 03 Jan 2006 03:44 AM (UTC) |
Message
| Apparently others have found similar solutions:
http://lua-users.org/wiki/SwitchStatement
The last one on the page is supposed to handle more complex situations, such as fall throughs and ranges of values. But I have to say, nasty surprises like this, where an otherwise good language (scripting or otherwise) is missing *basic* functions, drives me batty. Especially if they are ones I know I use fairly often. And this is one of those cases. :p | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #7 on Tue 03 Jan 2006 04:11 AM (UTC) Amended on Tue 03 Jan 2006 05:07 AM (UTC) by Nick Gammon
|
Message
| I referenced that Wiki item in my first reply. :)
Quote:
... an otherwise good language (scripting or otherwise) is missing *basic* functions ...
Well, C doesn't fully support it either, and I hope you aren't going to say C drives you batty. Take an example from the SMAUG source:
SPELL_FUN *spell_function( char *name )
{
if( !str_cmp( name, "spell_smaug" ) )
return spell_smaug;
if( !str_cmp( name, "spell_acid_blast" ) )
return spell_acid_blast;
if( !str_cmp( name, "spell_animate_dead" ) )
return spell_animate_dead;
if( !str_cmp( name, "spell_astral_walk" ) )
return spell_astral_walk;
... and so on ...
What is this doing? It is converting a name to a function, and it is doing it like this because the C switch statement doesn't handle strings, only numeric constants. There are 189 lines in this function (and it isn't the only one with this general design). Imagine how slow it is if the spell name you want is at the end of the list - or, not there at all.
In Lua you could have a simple table:
spellnames = {
spell_smaug = spell_smaug,
spell_acid_blast = spell_acid_blast,
spell_animate_dead = spell_animate_dead,
spell_astral_walk = spell_astral_walk,
-- and so on
}
This is Lua shorthand for keying a string literal to a value, in this case we assume a function. It is the same as:
spellnames = {
["spell_smaug"] = spell_smaug,
["spell_acid_blast"] = spell_acid_blast,
["spell_animate_dead"] = spell_animate_dead,
["spell_astral_walk"] = spell_astral_walk,
-- and so on
}
Now in Lua, you can do a direct table lookup, which is faster and neater. Plus, you can simply generate the "reverse" table in a couple of lines of code:
rev_spellnames = {}
for k, v in pairs (spellnames) do
rev_spellnames [v] = k
end
This generates, from the first table, a table which keys the function addresses to their names, so you can convert them back the other way.
You can't do that with a switch statement, which means that the C code (eg. SMAUG) tends to be a lot more cluttered, and stuff like adding new functions means you have to add them to a switch statement, *and* add them to the "reverse" function (a lengthy if statement) as well.
Effectively the table-driven approach forces you to think about what you are really trying to achieve and make a cleaner design. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Shadowfyr
USA (1,788 posts) Bio
|
Date
| Reply #8 on Tue 03 Jan 2006 04:37 PM (UTC) |
Message
| Oops. Didn't notice the reference. lol
But yeah, C some similar issues. But there are probably more efficient ways to impliment it is C than a lot of if then statements too, it would just be so esoteric that no one would understand it. lol Frankly, I haven't used C much, so didn't know about that limitation, but yeah, that would annoy me a tad to discover. Though, in C you would most likely use an enumerated list and then 'still' reference a number. | Top |
|
Posted by
| Mania
(1 post) Bio
|
Date
| Reply #9 on Wed 16 May 2007 05:14 AM (UTC) |
Message
| @Nick Gammon
The normal way to index a table with a key that no one else would use in lua is this:
default = {}
table = {default = "private value"}
By using that, you can nearly guarantee that it won't conflict.
=) | Top |
|
Posted by
| David Haley
USA (3,881 posts) Bio
|
Date
| Reply #10 on Wed 16 May 2007 05:31 AM (UTC) |
Message
| You'd actually need:
t = { [default] = "private value"}
otherwise you're just setting the string key "default".
Also, you'd probably make default a local variable to avoid polluting the global namespace, and to make sure that only the current chunk of code can see it. |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #11 on Wed 16 May 2007 07:49 AM (UTC) |
Message
| In that case it looks like my earlier post was wrong:
actions = {
[1] = action_1 ,
[2] = action_2_or_3 ,
[3] = action_2_or_3 ,
[\"a"] = action_a ,
DEFAULT = action_default
}
It should have read:
actions = {
[1] = action_1 ,
[2] = action_2_or_3 ,
[3] = action_2_or_3 ,
["a"] = action_a ,
[DEFAULT] = action_default
}
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Nick Gammon
Australia (23,121 posts) Bio
Forum Administrator |
Date
| Reply #12 on Wed 16 May 2007 07:51 AM (UTC) |
Message
| However, Mania is right, using a table as a key effectively gives you an address that should be unique. A possible problem with that is serializing the table, but that may have applied to the 1/0 case anyway. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| WillFa
USA (525 posts) Bio
|
Date
| Reply #13 on Wed 27 Aug 2008 08:14 PM (UTC) |
Message
| You could also set your Default action to the __index metatable, then you don't have to worry about another key stepping on it in the table.
| Top |
|
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.
48,555 views.
It is now over 60 days since the last post. This thread is closed.
Refresh page
top