User:Deco Da Man
From GMod Wiki
´'`\_(ò_Ó)_/´'` Tutorial storage (I'll make it a wikipage later):
This thread is supported by the Group For Less "LOL I COPYD FROM WIKI IT NOT WORK HALP PLES" And More "And x equals y plus z, old chaps.".
Note: I request an Lua Theory or Tutorial sub-forum :)
What are metatables?
If you aren't too up-to-date with the latest Jargon and Paradigms, the following explanation from the Lua Manual won't make much sense.
From the Lua Reference Manual: "Every value in Lua can have a metatable. This metatable is an ordinary Lua table that defines the behavior of the original value under certain special operations. You can change several aspects of the behavior of operations over a value by setting specific fields in its metatable."
So, I'm going to explain it.
I'm assuming you understand tables and how indexing (getting and setting) works. In a sequential form it'll be like this:
- Table indexed ("myTable[1]")
- Lua searches in the table for an entry with the key equal to the number 1.
- VALUE FOUND :): Lua returns the value
- VALUE NOT FOUND :(: Lua returns nil
Well you are wrong, dear friend. WRONG! Let me explain... If the value is not found, Lua searches for a metatable. This metatable is a secret thingy in the background that is a last resort. If Lua doesn't know how to do something with an object, it will give the object's metatable a chance to help it.
Now, let's have some code:
myTable = { a = 1, b = 2, } myMetaTable = {} function myMetaTable.__index(the_table, the_key) print(the_table, the_key) return 666 end setmetatable(myTable, myMetaTable)
Now, let's say I went "print(MyTable.a)". Lua would search the table, find it, say "WHOOP" and return 1, cause that's what the table contains! Durr! Now let's say I went "print(MyTable.c)" Lua would search the table, not find it, say "OH SHIT" and ask the metatable (if it has one, otherwise it would return nil (durr)). Now, the metatable function __index is what Lua calls. 'the_table' argument is the table that's been indexed and 'the_key' is the key that's trying to get read. You can do WHATEVER YOU WANT in this function. The value that you return is what the indexing function returns. In the end, Lua would print the table, the key and the value! :O
I hope you understand the BASICS of it.
But, that's pretty useless. I can do that with a function!
I just used __index to show you the BASICS of metatables, there's much, MUCH more! :D
Imagine you wanted to do this:
MyTable = setmetatable({1,2,3}, MyMetaTable) -- setmetatable returns the first argument after setting it's metatable print(MyTable(2)) -- prints 2 4 6
That's impossible! You can't call a table!
But you can! If you set the __call field of it's metatable, you can call it like a function! :O So, it might look something like:
function MyMetaTable.__call(the_table, an_argument) local new_table = {} for i,v in ipairs(the_table) do new_table[i] = v*an_argument end return unpack(new_table) end
And BAM! You can call the table! :O
WOAH! What else can I do with this?
HEAPS! :D Including operators! For example:
Table1 = setmetatable({66,55}, MyMetaTable) Table2 = setmetatable({33,99}, MyMetaTable) for i,v in ipairs( Table1 + Table2 ) do print(i, v) end --[[ prints: 1 66 2 55 3 33 4 99 ]]
Huh? Adding tables? What is this madness?
Madness? This... is... METATABLES! Metatables, my friend! By setting the __add field of an object's metatable, if Lua doesn't know how to add it, it will ask the metatable! The code I used:
function MyMetaTable.__add(the_table, the_other_table) local new_table = {} local count = 0 for i,v in ipairs(the_table) do count = count+1 new_table[count] = v end for i,v in ipairs(the_other_table) do count = count+1 new_table[count] = v end return new_table end
WOAHLY! But wait, what if you are adding two different objects, and both have __add?
Lua is smart ;) It will first check the left one, if it doesn't have an _add, it will go to the right one :D
Neat! What else can I do?
HEAPS! Here's a list. metamethod(arguments): explanation.
- add(left_object, right_object): the + operation.
- sub(left_object, right_object): the - operation. Behavior similar to the "add" operation.
- mul(left_object, right_object): the * operation. Behavior similar to the "add" operation.
- div(left_object, right_object): the / operation. Behavior similar to the "add" operation.
- mod(left_object, right_object): the % operation. Behavior similar to the "add" operation, with the operation o1 - floor(o1/o2)*o2 as the primitive operation.
- pow(left_object, right_object): the ^ (exponentiation) operation. Behavior similar to the "add" operation, with the function pow (from the C math library) as the primitive operation.
- unm(object): the unary - operation.
- concat(left_object, right_object): the .. (concatenation) operation.
- len(object): the # operation.
- eq(left_object, right_object): the == operation. The function getcomphandler defines how Lua chooses a metamethod for comparison operators. A metamethod only is selected when both objects being compared have the same type and the same metamethod for the selected operation.
- lt(left_object, right_object): the < operation.
a > b is equivalent to b < a.
- le(left_object, right_object): the <= operation.
a >= b is equivalent to b <= a. Note that, in the absence of a "le" metamethod, Lua tries the "lt", assuming that a <= b is equivalent to not (b < a).
- index(object, key): The indexing access table[key].
- newindex(object, key, value): The indexing assignment table[key] = value.
- call(object, ...): called when Lua calls a value. ... is the arguments passed to the function.
- tostring(object): called by the tostring function, return what you want it to return (this is not a proper metamethod).
(Slightly copy'n'pasta from the manual, edited alot.)
This is so cool! But what are they usful for in application?
Alot!
Take memorising tables!
Huh?
Imagine you had a function that was VERY intense, called VeryIntenseFunction. Let's say the user enters numbers, and you want to give the answer to the user using VeryIntenseFunction. Let's say it could take a few minutes to work out the answer, but the user needs it quickly! Well, at first, you are stuffed. It needs to be worked out. But, if the user enters, let's say, '5' many times. Why work it out twice? Why not just store the answer? Well, that's what memorising tables are :D
It looks like this:
function VeryIntenseFunction(n) for i = 1,9000 do n = n+math.random(1, 9000)/10-math.random(1,1000) end for i = 1,9000 do n = n*0.1*10*0.1*10*0.1*10*0.1*10*0.1*10 end return n end local MyMetaTable = { __index = function(object, key) local n = VeryIntenseFunction(key) object[key] = n return n end }
That's the base of a memorizing table. The first time the user enters '5', it'll have to work it out. But the second time, it will just get it from the storage! If you remember from earlier, I said that Lua only calls metamethods if it can't figure it out for itself. So what we do, is store the computed value in the table. The metamethod is only called if you want a value that hasn't been worked out yet! It's a memory! Neat, huh?
Very neat! But I've tried to set up a memorising table, and failed horribly :(
Here ya go:
-- Setup local MyMetaTable = { __index = function(object, key) local n = --the_value object[key] = n return n end } -- Usage MyTable = setmetatable({}, MyMetaTable) print(MyTable[1]) -- SLLLOOOOW print(MyTable[2]) -- SLLLOOOOW print(MyTable[1]) -- ZOOOOOOOM
Give me an example of an awesome-super-epic-meta-table-that-will-solve-all-my-coding-problems!
The super-epic-...-table is up to YOU to make! But a simple example class will come soon.
I WANT MORE :(
MORE WILL COME!
By the way, why are they called metatables?
Cause meta means data about data! :D
Well, at least Wikipedia says so.
That means if you set the metatable of a metatable, it's a metametatable or, data about data about data.
And if you set the metatable of a metatable of a metatable...
SHUT UP ALREADY!
Ok then... :(
I hope you enjoyed my tutorial, and learnt something! :eng101:
I did!
Good!
Bye!
Good bye!
smartass...
I'm still here!
OH! Opps...
...