|
|
(55 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
| ==Metatables==
| | {{User:Merlin11188/Templates/NoEdit}} |
| {{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}} | |
| ===What is a Metatable?===
| |
|
| |
|
| Metatables allow tables to become more powerful than before.
| |
| You may think that if you have code like this:
| |
|
| |
|
| <pre>
| | {{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/> |
| </pre> | | |} |
| | __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 and see if there's a metatable attached to the list and then return nil if
| |
| there isn't one.
| |
|
| |
|
| ===get and setmetatable=== | | ===Character and Notifications=== |
| There are two main functions when dealing with metatables: getmetatable and
| | {| |
| setmetatable. You use setmetatable to give a table a metatable, and you use
| | |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. |
| getmetatable to retrieve the metatable of an object. Here's an example:
| | |[[File:Avatar_Notifications.png|frame|A picture of [[My_Character|your character]] with system notifications beneath it.]] |
| <pre>
| | |} |
| x={}
| |
| metaTable={}
| |
| setmetatable(x, metaTable) --Give x a metatable called metaTable!
| |
| print(getmetatable(x))
| |
|
| |
|
| Output:
| | ===Best Friends=== |
| table: [hexadecimal digits]
| | {| |
| </pre>
| | |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!]] |
| | |} |
|
| |
|
| | ===Status Update=== |
| | {| |
| | |You can use this so that people who visit your profile can see what you're up to. |
| | |[[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]]!]] |
| | |} |
|
| |
|
| ===Metatable Demonstration=== | | ===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.]] |
| | |} |
|
| |
|
| {{Example|1=<i></i>
| | ===Recently Played Games=== |
| local List={1,2}<br/>
| | {| |
| print("In table List, index z is "..List.z)<br/>
| | |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. |
| Output:<br/><font color="red">attempt to concatenate field 'z' (a nil value)</font color>
| | |[[File:Recently_Played_Games.png|frame|This is a list of your most recently played [[game]]s.]] |
| }}
| | |} |
|
| |
|
| z is nil. Now, look at what happens with metatables:
| | ===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!]] |
| | |} |
|
| |
|
| {{Example|<pre>
| | <!-- |
| local List={1,2} -- A normal table
| | {| class="wikitable" style="border-spacing: 0px; padding: 0px;" |
| local Metatable={ -- A metatable
| | |- |
| __index=function(Table, Index)
| | |[[File:Avatar + Notifications]] |
| rawset(Table, Index, 0) -- Set Table[Index] to 0
| | |[[File:Status_Update.png]] |
| return Table[Index] -- return Table[Index] (which is now 0)
| | |[[File:Recently_Played_Games.png|287px]] |
| end
| | |- |
| }
| | |[[File:Best_Friends.png]] |
| setmetatable(List, Metatable) -- Now List has the metatable Metatable
| | |<div style="top:0px;">[[File:Feed_bar.png]]</div> |
| print("In table List, index z is "..List.z)
| | |[[File:FacebookConnect_Unconnected.png]] |
| | | |}--> |
| Output:
| |
| In table List, index z is 0
| |
| </pre>}}
| |
| | |
| What happened there? List is a normal table, with nothing unusual about it. Metatable is
| |
| also a table, but it has something special: the __index metamethod. The __index metamethod
| |
| is fired when Table[Index] is nil; or, in this case, List.z is nil. Now, nothing would happen
| |
| because the two tables (List and Metatable) aren't linked. That's what the third line does:
| |
| sets List's metatable to Metatable, which means that when List is indexed (__index) in an
| |
| index that's nil (List.z), the function associated with __index in Metatable is run. The
| |
| function with __index in Metatable uses rawset to make a new value in the Table (or List).
| |
| Then, that value is returned. So, List.z is set equal to 0, and then List.z is returned
| |
| (which is 0).
| |
| | |
| ===Metamethods===
| |
| | |
| 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
| |
| of metamethods that can be used:
| |
| | |
| *__index(Table, Index) — Fires when Table[Index] is nil
| |
| *__newindex(Table, Index, Value) — Fires at Table[Index] = Value when Table[Index] is nil
| |
| *__call(Table, arg) — Allows the table to be used as a function, arg is the arguments passed
| |
| *__concat(Table, Object) — The .. concatenation operator.
| |
| *__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˚
| |
| *__gc(Object) - Fired when the Object is garbage-collected
| |
| *__len(Object) - Fired when the # operator is used on the Object.
| |
| | |
| ˚ 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 operator to make the table negative:
| |
| | |
| {{Example|<pre>
| |
| local Table1 = {10,11,12}
| |
| | |
| local Metatable = {
| |
| __unm = function (Table) -- __unm is for the unary operator -
| |
| local t={} -- the table to return
| |
| for i,v in pairs(Table) do
| |
| t[i]=-v -- Set t[i] to -1*Table[i]
| |
| end
| |
| return t -- return the negative Table!
| |
| end
| |
| }
| |
| setmetatable(Table1, Metatable)
| |
| print(table.concat(-Table1, "; "))
| |
| | |
| Output:
| |
| -10; -11; -12
| |
| </pre>}}
| |
| | |
| Now, you can easily do that with a simple function, but there's a lot more where
| |
| that came from. Take this for example:
| |
| | |
| <pre>
| |
| Table = {10,20,30}
| |
| print(Table(5))
| |
| </pre>
| |
| | |
| Now, obviously you can't call a table. That's just crazy, but (surprise, surprise!)
| |
| with metatables you can.
| |
| | |
| {{Example|<pre>
| |
| local Table = {10,20,30}
| |
| | |
| local Metatable = {
| |
| __call = function (table, param)
| |
| local t = {}
| |
| for i, v in ipairs(table) do
| |
| t[i] = v + param -- Add the argument (5) to the value, then place it in the new table (t).
| |
| end
| |
| return unpack(t) -- Return a string version of t.
| |
| end
| |
| }
| |
| | |
| setmetatable(Table, Metatable)
| |
| print(Table(5))
| |
| | |
| Output:
| |
| 15 25 35
| |
| </pre>}}
| |
| | |
| You can do a lot more as well, such as adding tables!
| |
| | |
| {{Example|<pre>
| |
| local Table1 = {10,11,12}
| |
| local Table2 = {13,14,15}
| |
| | |
| for k, v in pairs(Table1 + Table2) do
| |
| print(k, v)
| |
| end
| |
| </pre>}}
| |
| | |
| This will error saying that you're attempting to perform arithmetic on a table. Let's try this with a metatable.
| |
| | |
| {{Example|<pre>
| |
| local Table1 = {10,11,12}
| |
| local Table2 = {13,14,15}
| |
| | |
| local Metatable = {
| |
| __add = function (table1, table2)
| |
| local t = {}
| |
| for i, v in ipairs(table1) do
| |
| t[i]=v+table2[i] -- t[i] is equal to table1[i]+table2[i]
| |
| end
| |
| return t
| |
| 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
| |
| </pre>}}
| |
| | |
| | |
| | |
| 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|<pre>
| |
| 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
| |
| </pre>}}
| |
| | |
| ===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|<pre>
| |
| 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.
| |
| </pre>}} | |
| | |
| ===See Also===
| |
| | |
| http://www.lua.org/manual/5.1/manual.html#2.8
| |
| | |
| http://www.lua.org/pil
| |
| | |
| [[Category:Scripting_Tutorials]] | |