Register forum user name Search FAQ

Gammon Forum

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

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

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.