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.
Due to spam on this forum, all posts now need moderator approval.
Entire forum
➜ MUSHclient
➜ Lua
➜ Using metatables to make attempts to index nil return false
|
Using metatables to make attempts to index nil return false
|
It is now over 60 days since the last post. This thread is closed.
Refresh page
| Posted by
| Bobble
Canada (76 posts) Bio
|
| Date
| Fri 28 Nov 2008 04:09 AM (UTC) |
| Message
| Greets everyone,
I've run into a bit of a snag and I believe that metatables may hold the answer for me. However, I'm a little unsure how to go about getting what I need done.
Part of one of my functions looks something like this:
if characters_online.elf.warrior then
Note ("Yay, there's at least one elf warrior online!")
end
The problem is, with the way I've set up my tables, if there's no elves online at all the characters_online.elf key doesn't exist and if this function is called I get an error stating that I tried to index a nil value.
What I would prefer is to have a situation where if the characters_online.elf key is nil, it returns a boolean false rather than an error.
Does anyone have any idea about how this might be achieved? |
Open the watch. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #1 on Fri 28 Nov 2008 04:53 AM (UTC) |
| Message
| One simple way, assuming your script is not more complex than you have shown, is to do this:
characters_online.elf = characters_online.elf or {}
if characters_online.elf.warrior then
Note ("Yay, there's at least one elf warrior online!")
end
The extra line at the start will make the table for elf, if it isn't there, and then you can test if a warrior is on. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Bobble
Canada (76 posts) Bio
|
| Date
| Reply #2 on Fri 28 Nov 2008 01:38 PM (UTC) |
| Message
| Excellent, this works well.
Thanks Nick. Your help is always appreciated. |
Open the watch. | | Top |
|
| Posted by
| Erendir
Germany (47 posts) Bio
|
| Date
| Reply #3 on Sun 30 Nov 2008 11:52 PM (UTC) |
| Message
| You can also write:
if characters_online.elf and characters_online.elf.warrior then
Note ("Yay, there's at least one elf warrior online!")
end
it's same as
if characters_online.elf then
if characters_online.elf.warrior then
Note ("Yay, there's at least one elf warrior online!")
end
end
| | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #4 on Mon 01 Dec 2008 03:25 AM (UTC) Amended on Mon 01 Dec 2008 03:27 AM (UTC) by Nick Gammon
|
| Message
| Or this:
if (characters_online.elf or {} ) .warrior then
Note ("Yay, there's at least one elf warrior online!")
end
This is slightly less efficient as it potentially creates an empty table, Erendir's idea is better in the sense that it tests for the presence of an elf entry without creating one. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #5 on Mon 01 Dec 2008 03:31 AM (UTC) |
| Message
| Another approach is to use a metatable. This would be more general as it would handle dwarves, humans, etc. as well as elves.
-- in initialization ...
characters_online = {}
setmetatable (characters_online, { __index = function () return {} end } )
-- later on ...
if characters_online.elf.warrior then
Note ("Yay, there's at least one elf warrior online!")
end
The metatable above for characters_online returns an empty table if you try to access a non-existent index. Thus, for any race at all you will get an empty table, unless the entry already exists. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Bobble
Canada (76 posts) Bio
|
| Date
| Reply #6 on Tue 30 Dec 2008 07:33 PM (UTC) |
| Message
| Thanks everyone for the help on this, but I'm having a bit of a problem with metatables again.
I have a large table that lists all the various maladies I can be given in combat (it's an IRE MUD). This table lists the name of each affliction, it's specific cure, it's cure type and a bunch of other information. It looks like this:
cures = {
{ name = "stupidity" , cure = "goldenseal" , type = "herb" , given_by_whisper = "y"},
{ name = "anorexia" , cure = "epidermal" , type = "salve", given_by_whisper = "y"},
{ name = "sensitivity" , cure = "kelp" , type = "herb" , given_by_whisper = "y"},
{ name = "asthma" , cure = "kelp" , type = "herb" , given_by_whisper = "n"}
}
I use this table (which is actually much larger) to create other tables that use things cure as the key with the maladies that are healed by that cure as the values.
For instance, I do something like this:
whisper_index = {}
for _, v in ipairs(cures)
if v.given_by_whisper == "y" then
whisper_index.type [v.type] = true
whisper_index.cure [v.cure] = true
end
end
Of course, doing it this way gives me an error as the "for" iteration starts, it tries to index whisper_index.type which is a nil value, because it hasn't been defined yet.
Now, a simple solution is to define
However, I'm making a lot of these indexes and I was thinking that I could use a metatable to create a situation where if a lua is asked to index a table that isn't there (such as above), it would create an empty table.
I tried this, thinking it would work, unfortunately, it didn't:
mt = {__index = function () return {} end}
whisper_index = {}
setmetatable(whisper_index, mt)
When the "for" chunk above goes through, all that I get is an empty whisper_index.names table.
Does anyone know where I've gone wrong here? I know there might be more efficient ways to do what I want to do, but I'm treating this as an exercise in learning how metatables and the __index function work.
Please let me know if you need further clarification or more details about what I'm trying to achieve.
|
Open the watch. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #7 on Tue 30 Dec 2008 07:46 PM (UTC) |
| Message
| What the metatable does is return a value in place of the missing one. However it doesn't put it there. For example:
mt = {__index = function () return 5 end}
whisper_index = {}
setmetatable(whisper_index, mt)
print (whisper_index ["nick"]) --> 5
This indeed prints 5 for the missing value. However it has not made whisper_index ["nick"] hold the value 5, which I think is what you are trying to do.
As far as I can see, the simplest way would be to make the subtables like this:
whisper_index = { type = {}, cure = {}, }
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Bobble
Canada (76 posts) Bio
|
| Date
| Reply #8 on Tue 30 Dec 2008 09:00 PM (UTC) |
| Message
| Thanks for clearing that up Nick.
Out of curiosity, is there a way to make the metatable do what I was trying to get it do? Ie. Give whisper_index["Nick"] the value of 5? |
Open the watch. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #9 on Tue 30 Dec 2008 09:17 PM (UTC) |
| Message
| Well this seemed to do the trick:
mt = { __index = function (tbl, key)
local newitem = {}
tbl [key] = newitem
return newitem
end
}
whisper_index = {}
setmetatable(whisper_index, mt)
whisper_index.blah = 42
print (whisper_index.blah) --> 42
The __index function gets the table and key we are trying to set, so I have used that to create an empty table, assign it to the target table, and return the new, empty, table, so that it can be used first time through. Second time through the key will exist, and __index won't have to be called.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Bobble
Canada (76 posts) Bio
|
| Date
| Reply #10 on Wed 31 Dec 2008 12:35 AM (UTC) |
| Message
| Once again Nick, thanks for your help. This let me do what I was aiming to do. I very much appreciate it.
Take care! |
Open the watch. | | 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.
35,956 views.
It is now over 60 days since the last post. This thread is closed.
Refresh page
top