User:Merlin11188/Draft: Difference between revisions

From Legacy Roblox Wiki
Jump to navigationJump to search
>Merlin11188
No edit summary
>Merlin11188
Replacing page with '===Weak Tables=== ---- {{EmphasisBox|Note: Weak Tables require knowledge of Scope and Metatables. <br/> Also, all Lua objects in the global environment are ignored and ...'
Line 1: Line 1:
==Metatables==
===Weak Tables===
{{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?===
{{EmphasisBox|Note: Weak Tables require knowledge of [[Scope]] and [[Metatables]]. <br/>
 
Also, all Lua objects in the global environment are ignored and will never be garbage collected.|red|dark=yes}}
Metatables allow tables to become more powerful than before.
You may think that if you have code like this:
 
<pre>
local list = {1,2}
print(list[3])
</pre>
 
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===
There are two main functions when dealing with metatables: getmetatable and
setmetatable. You use setmetatable to give a table a metatable, and you use
getmetatable to retrieve the metatable of an object. Here's an example:
<pre>
x={}
metaTable={}
setmetatable(x, metaTable) --Give x a metatable called metaTable!
print(getmetatable(x))
 
Output:
table: [hexadecimal digits]
</pre>
 
===Metatable Demonstration===
 
{{Example|1=<i></i>
local List={1,2}<br/>
print("In table List, index z is "..List.z)<br/>
Output:<br/><font color="red">attempt to concatenate field 'z' (a nil value)</font color>
}}
 
z is nil. Now, look at what happens with metatables:
 
{{Example|<pre>
local List={1,2} -- A normal table
local Metatable={ -- A metatable
__index=function(Table, Index)
    rawset(Table, Index, 0) -- Set Table[Index] to 0
    return Table[Index] -- return Table[Index] (which is now 0)
end
}
setmetatable(List, Metatable) -- Now List has the metatable Metatable
print("In table List, index z is "..List.z)
 
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, Table(arg)
*__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]]

Revision as of 20:54, 10 July 2011

Weak Tables


Note: Weak Tables require knowledge of Scope and Metatables.
Also, all Lua objects in the global environment are ignored and will never be garbage collected.