User:Merlin11188/Draft: Difference between revisions

From Legacy Roblox Wiki
Jump to navigationJump to search
>Merlin11188
Metatables: Increased compatibility with the new orders slightly
>Merlin11188
Added a picture; added locations to the descriptions; reordered slightly
 
(20 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{EmphasisBox|The metatables for [[Strings]] and all ROBLOX types are locked; however, in normal Lua (not RBX.lua) you can set the metatables of these objects using the debug library.[http://www.lua.org/manual/5.1/manual.html#5.9] |green|dark=yes}}
{{User:Merlin11188/Templates/NoEdit}}
==What is a Metatable?==


Metatables allow tables to become more powerful than before. They are attached to data and contain values called Metamethods. Metamethods are fired when a certain action is used with the datum that it is attached to.
You may think that if you have code like this:


<code lua>
{{Stub}}
local list = {1, 2}
{|
print(list[3])
|[[File:Home_Subpage.png|frame|A picture of the 'Home' tab on the submenu in 'My ROBLOX'.]] <br/>
</code>
|}
__TOC__


The code will search through the list for the third index in list, realize it's
not there, and return nil. That's totally wrong. What really happens is the code
will search through the list for the third index, realize it's not there, and then
try to see if there's a metatable attached to the table, returning nil if
there isn't one.


==setmetatable() and getmetatable()==
===Character and Notifications===
The two primary functions for giving and finding a table's metatable are setmetatable() and getmetatable().
{|
<code lua>
|On the far left of the page there is a picture of [[My_Character|your character]]. Underneath, there is a link to your system notifications.
local x = {}
|[[File:Avatar_Notifications.png|frame|A picture of [[My_Character|your character]] with system notifications beneath it.]]
local metaTable = {}      -- metaTables are tables, too!
|}
setmetatable(x, metaTable) -- Give x a metatable called metaTable!
print(getmetatable(x)) --> table: [hexadecimal memory address]
</code>


==Metatable Demonstration==
===Best Friends===
{|
|Underneath of your avatar and notification box is your [[Friends#Best_Friends|best friends]] list. Here you can see what your best friends are doing (from their [[My_Home#Status_Update|shout box]]) and whether or not they're online.
|[[File:Best_Friends.png|frame|This a list of your [[Friends#Best_Friends|best friends]] and their most recent shouts!]]
|}


{{Example|<code lua>
===Status Update===
local list = {1, 2}
{|
print("In table List, key \"z\" is "..tostring(list.z)) --> In table List, key "z" is nil
|You can use this so that people who visit your profile can see what you're up to.
</code>
|[[File:Status Update.png|frame|This is the status update bar. People who have you as their [[Friends#Best_Friends|best friend]] will see it on their [[My_Home|home]]!]]
|}


Now, look at what happens with metatables:
===Feed===
{|
|Your feed is in the center of the page, just beneath your status update box. Your feed is a way to keep you updated with all of your groups. Every time someone uses the shout box in one of your [[groups]], you're updated here!
|[[File:Feed bar.png|frame|This is for your feed—all of your [[groups]]' shouts go here.]]
|}


<code lua>
===Recently Played Games===
local list = {1, 2}      -- A normal table
{|
local metatable = {      -- A metatable
|On the far right is the 'Recently Played Games' box. Your most recently played games can be seen here. If you want a larger list, you can click the '''See More''' button.
__index = function(t, key)
|[[File:Recently_Played_Games.png|frame|This is a list of your most recently played [[game]]s.]]
rawset(t, key, 0) -- Set t[key] to 0
|}
return t[key]    -- return t[key] (which is now 0)
end
}
setmetatable(list, metatable) -- Now list has the metatable metatable
print("In table List, key \"z\" is "..tostring(list.z)) --> In table List, key "z" is 0
</code>}}


What happened there? <tt>list</tt> is a normal table, with nothing unusual about it. <tt>metatable</tt> is also a table, but it has something special: the <tt>__index</tt> metamethod. The <tt>__index</tt> metamethod is fired when <tt>t[key]</tt> is nil, or in this case, <tt>list.z</tt> is nil. Now, nothing would happen because the two tables (<tt>list</tt> and <tt>metatable</tt>) aren't linked. That's what the third line does: sets <tt>list</tt>'s metatable to <tt>metatable</tt>, which means that when <tt>list</tt> is indexed (<tt>__index</tt>) at an index that's nil (<tt>list.z</tt>), the function associated with <tt>__index</tt> in Metatable is run. The function at <tt>__index</tt> in <tt>metatable</tt> uses <tt>rawset</tt> to make a new value in the Table (or <tt>list</tt>). Then, that value is returned. So, <tt>list.z</tt> is set to 0, and then <tt>list.z</tt> is returned (which is 0).
===Facebook Connect===
{|
|On the far right, underneath of the 'Recently Played Games' box is the Facebook connect box. If you have a Facebook account, you can link it to your ROBLOX account! See [[connecting your account to Facebook]] for more info.
|[[File:FacebookConnect_Unconnected.png|frame|Facebook connect. You can use this to link your Facebook account to your [[Roblox|ROBLOX]] account! Your personal info will '''not''' be shared with other users!]]
|}


==Metamethods==
<!--
 
{| class="wikitable" style="border-spacing: 0px; padding: 0px;"
Metamethods are the functions that are stored inside a metatable. They can go from
|-
calling a table, to adding a table, to even dividing tables as well. Here's a list
|[[File:Avatar + Notifications]]
of metamethods that can be used:
|[[File:Status_Update.png]]
 
|[[File:Recently_Played_Games.png|287px]]
*__index(Table, Index) — Fires when Table[Index] is nil.
|-
*__newindex(Table, Index, Value) — Fires when Table[Index] = Value when Table[Index] is nil.
|[[File:Best_Friends.png]]
*__call(Table, arg) — Allows the table to be used as a function, arg is the arguments passed, Table(arg).
|<div style="top:0px;">[[File:Feed_bar.png]]</div>
*__concat(Table, Object) — The .. concatenation operator.
|[[File:FacebookConnect_Unconnected.png]]
*__unm(Table) — The unary – operator.
|}-->
*__add(Table, Object) — The + addition operator.
*__sub(Table, Object) — The – subtraction operator.
*__mul(Table, Object) — The * mulitplication operator.
*__div(Table, Object) — The / division operator.
*__mod(Table, Object) — The % modulus operator.
*__pow(Table, Object) — The ^ exponentiation operator.
*__tostring(Table) — Fired when tostring is called on the table.
*__metatable — if present, locks the metatable so getmetatable will return this instead of the metatable and setmetatable will error. Non-function value.
*__eq(Table, Table2) — The == equal to operator˚
*__lt(Table, Table2) — The < less than operator˚
*__le(Table, Table2) — The <= operator˚
*__mode — Used in [[Weak Tables]], declaring whether the keys and/or values of a table are weak.
*__gc(Object) — Fired when the Object is garbage-collected.
*__len(Object) — Fired when the # operator is used on the Object. '''NOTE:''' Only userdatas actually respect the __len() metamethod, this is a bug in Lua 5.1
 
˚ Requires two tables; does not work with a table and a number, string, etc. The tables must have the '''same''' metatable.
 
==Using Metatables==
 
There are many ways to use metatables, for example the __unm metamethod (to make a table negative):
{{Example|<code lua>
local table1 = {10,11,12}
local metatable = {
__unm = function(t) -- __unm is for the unary operator -
local negatedTable = {} -- the table to return
for key, value in pairs(t) do
negatedTable[key] = -value
end
return negatedTable -- return the negative Table!
end
}
 
setmetatable(table1, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
</code>}}
 
Here's an interesting way to declare things using __index:
{{Example|<code lua>
local t = {}
local metatable = {
__index = {x = 1}
}
setmetatable(t, metatable)
print(t.x) --> 1
</code>}}
 
__index was fired when x was accessed from the table. __index then defined x as 1 instead of nil; therefore, 1 was returned.
 
Now you can easily do that with a simple function, but there's a lot more where
that came from. Take this for example:
<code lua>
local table = {10, 20, 30}
print(table(5))
</code>
 
Now, obviously you can't call a table. That's just crazy, but (surprise, surprise!)
with metatables you can.
 
{{Example|<code lua>
local Table = {10, 20, 30}
local metatable = {
__call = function(Table, param)
local sum = {}
for i, value in ipairs(Table) do
sum[i] = value + param -- Add the argument (5) to the value, then place it in the new table (t).
end
return unpack(sum) -- Return the individual table values
end
}
 
setmetatable(Table, metatable)
print(Table(5)) --> 15 25  35
</code>}}
 
You can do a lot more as well, such as adding tables!
{{Example|<code lua>
local table1 = {10, 11, 12}
local table2 = {13, 14, 15}
 
for k, v in pairs(table1 + table2) do
print(k, v)
end
</code>
 
This will error saying that you're attempting to perform arithmetic on a table.  Let's try this with a metatable.
<code lua>
local table1 = {10, 11, 12}
local table2 = {13, 14, 15}
 
local metatable = {
__add = function(table1, table2)
local sum = {}
for key, value in pairs(table1) do
if table2[key] ~= nil then -- Does this key exist in that table?
sum[key] = value + table2[key]
else                      -- If not, add 0.
sum[key] = value
end
end
    -- Add all the keys in table2 that aren't in table 1
for key, value in pairs(table2) do
if sum[key] == nil then
sum[key] = value
end
end
return sum
end
}
setmetatable(table1, metatable)
setmetatable(table2, metatable)
 
for k, v in pairs(table1 + table2) do
print(k, v)
end
 
--[[
Output:
 
1 23
2 25
3 27
]]
</code>}}
 
If the two tables have two different __add functions, then Lua will go to table1
first and if it doesn't have an __add function, then it'll go to the second one. That
means that you really only have to set the metatable of Table1 or Table2, but it's
nicer and more readable to set the metatable of both.
 
Here is one last example breaking away from using separate variables when it isn't necessary.
{{Example|<code lua>
local t = setmetatable({
10, 20, 30
}, {
__call = function(a, b)
return table.concat(a, b .. ' ') .. b
end
})
print('Tables contains '..t(1)) --> Tables contains 101 201 301
</code>}}
 
==rawset(), rawget(), rawequal()==
If you continue to play with metatables, you might encounter problems. Here's a primary example:
 
Let's say that we want to make a table error() whenever you try to access a nil value in it.
<code lua>
local planets = {Earth = "COOL!"}
local mt = {
__index = function(self, i)
return self[i] or error(i .. " is not a real Planet!") -- If self[i] is nil, then error!
end
}
 
setmetatable(planets, mt);
print(planets.Earth);    --> COOL!
print(planets.Pluto);    --> error: C stack overflow
</code>
 
So what does <tt>C stack overflow</tt> mean?
It means you've called the same function too many times inside of itself (or recursed too deeply). You called a function too many times too quickly! The normal limit is actually right around 20,000!
 
So what caused the <tt>__index()</tt> method to call itself ?
The answer: <tt>self[i]</tt>
 
So what can we do? How can we get a member of a table without calling it's <tt>__index()</tt> method?
With [[Function_Dump/Core_Functions#rawget_.28table.2C_index.29|rawget()]], of course!
 
 
{{EmphasisBox|[[Function_Dump/Core_Functions#rawget_.28table.2C_index.29|rawget()]], [[Function_Dump/Core_Functions#rawset_.28table.2C_index.2C_value.29|rawset()]], and [[Function_Dump/Core_Functions#rawequal_.28v1.2C_v2.29|rawequal()]] are very important when making custom metatables because they do not invoke a table's metamethods, so things like __index and __newindex aren't activated!}}
 
 
Let's make a small change to the code and try again!
<code lua>
local planets = {Earth = "COOL!"}
local mt = {
__index = function(self, i)
return rawget(self, i) or error(i .. " is not a real Planet!")
end
}
setmetatable(planets, mt)
print(planets.Earth)    --> COOL!
print(planets.Pluto)    --> error: Pluto is not a real Planet!
</code>
 
 
 
==Use Cases==
 
Now, I am well aware that you can do all of these as a simple function yourself,
but there's a lot more than what you think it can do. Let's try a simple program
that will memorize a number when a possibly laggy math problem is put into it.
 
For this one we will be using the __index metamethod just to make it simple:
 
{{Example|<code lua>
local Table = {}
 
local function mathProblem(num)
for i = 1, 20 do
num = math.floor(num * 10 + 65)
end
for i = 1, 10 do
num = num + i - 1
end
return num
end
 
local Metatable = {
__index = function (object, key)
local num = mathProblem(key)
object[key] = num
return num
end
}
 
local setmetatable(Table, Metatable)
 
print(Table[1]) -- Will be slow because it's the first time using this number.
print(Table[2]) -- will be slow because it's the first time using this number.
print(Table[1]) -- will be fast because it's just grabbing the number from the table.
</code>}}
 
==See Also==
 
http://www.lua.org/manual/5.1/manual.html#2.8
 
http://www.lua.org/pil
 
[[Category:Scripting_Tutorials]]

Latest revision as of 03:31, 12 March 2012

Do not edit!
The creator of this subpage does not want it to be edited without permission. Please discuss any changes that you think are relevant on the talk page.


Stub icon Stub
This article is a stub. If you think you know more about this than we do, please help us by contacting a writer.
Thank you!


A picture of the 'Home' tab on the submenu in 'My ROBLOX'.


Character and Notifications

On the far left of the page there is a picture of your character. Underneath, there is a link to your system notifications.
A picture of your character with system notifications beneath it.

Best Friends

Underneath of your avatar and notification box is your best friends list. Here you can see what your best friends are doing (from their shout box) and whether or not they're online.
This a list of your best friends and their most recent shouts!

Status Update

You can use this so that people who visit your profile can see what you're up to.
This is the status update bar. People who have you as their best friend will see it on their home!

Feed

Your feed is in the center of the page, just beneath your status update box. Your feed is a way to keep you updated with all of your groups. Every time someone uses the shout box in one of your groups, you're updated here!
This is for your feed—all of your groups' shouts go here.

Recently Played Games

On the far right is the 'Recently Played Games' box. Your most recently played games can be seen here. If you want a larger list, you can click the See More button.
This is a list of your most recently played games.

Facebook Connect

On the far right, underneath of the 'Recently Played Games' box is the Facebook connect box. If you have a Facebook account, you can link it to your ROBLOX account! See connecting your account to Facebook for more info.
Facebook connect. You can use this to link your Facebook account to your ROBLOX account! Your personal info will not be shared with other users!