Difference between revisions of "Lua: Methods"
m (this is before i knew what i was doing) |
|||
Line 5: | Line 5: | ||
[[Lua]]'s methods have a special syntax; you don't ''need'' to use this syntax, but it's cleaner and easier than the alternative. Called ''colon syntax'', it of course uses a colon instead of a dot: | [[Lua]]'s methods have a special syntax; you don't ''need'' to use this syntax, but it's cleaner and easier than the alternative. Called ''colon syntax'', it of course uses a colon instead of a dot: | ||
− | + | <source lang="lua" enclose="div">-- regular function | |
− | table. | + | table.function_call() |
-- method | -- method | ||
− | table: | + | table:method_call()</source> |
Basically, by using the colon, you insert the data structure immediately left of the colon as a hidden first parameter. Therefore, these two calls are equivalent: | Basically, by using the colon, you insert the data structure immediately left of the colon as a hidden first parameter. Therefore, these two calls are equivalent: | ||
− | + | <source lang="lua" enclose="div">table.method(table, other_param) | |
table:method(other_param) | table:method(other_param) | ||
− | </ | + | </source> |
− | When you're writing the source code, however, that first parameter will generally be something else. It's just a regular variable, so it doesn't necessarily need to have the same name as the data structure calling the method ([[Lua: Scoping|you should avoid this, in fact]]). You'll often see this variable named | + | When you're writing the source code, however, that first parameter will generally be something else. It's just a regular variable, so it doesn't necessarily need to have the same name as the data structure calling the method ([[Lua: Scoping|you should avoid this, in fact]]). You'll often see this variable named <tt>self</tt>, as this is the variable name Lua uses if the method definition does not explicitly state another name for its first parameter. |
===Table Methods=== | ===Table Methods=== | ||
− | In order to use methods for any given table, we need to give it a [[Lua: Metatables|metatable]] | + | In order to use methods for any given table, we need to give it a [[Lua: Metatables|metatable]]. Because we don't want the methods to actually "pollute" this table, we will have them stored in a separate table called ''methods''. So our code should start out like this: |
− | + | <source lang="lua" enclose="div">methods = { } | |
− | function newTable( | + | function newTable(t_data) |
− | + | return setmetatable(t_data, methods) | |
− | + | end</source> | |
− | |||
− | |||
− | end</ | ||
− | |||
− | Now we need to write a method for the | + | Now we need to write a method for the <tt>methods</tt> table. A useful one will return the length of the ''entire'' table, including non-numeric keys, which are normally ignored by the length operator. So our <tt>methods</tt> table will look like: |
− | + | <source lang="lua" enclose="div">methods = { | |
length = function (self) | length = function (self) | ||
local l = 0 | local l = 0 | ||
Line 40: | Line 36: | ||
return l | return l | ||
end, | end, | ||
− | }</ | + | } |
− | This is a similar table iteration as the | + | methods.__index = methods</source> |
− | + | This is a similar table iteration as the <tt>__add</tt> metamethod from the Metatables tutorial. However, instead of adding the values at two tables' indexes, we're just incrementing a variable to use as our length. Note how we are using <tt>string.sub</tt> to exclude key names starting with '__'; we don't want metamethods to be counted in the length! We also define <tt>methods</tt>' <tt>__index</tt> metamethod to point to itself, so Lua knows this is the table that has the methods we need. We're actually done now, so we should try testing it: | |
+ | <source lang="lua" enclose="div">methods = { | ||
length = function (self) | length = function (self) | ||
local l = 0 | local l = 0 | ||
Line 53: | Line 50: | ||
end, | end, | ||
} | } | ||
+ | methods.__index = methods | ||
− | newTable | + | function newTable(t_data) |
− | + | return setmetatable(t_data, methods) | |
− | |||
− | |||
− | |||
end | end | ||
Line 66: | Line 61: | ||
print(test.length(test)) -- prints 6 (longhand for above) | print(test.length(test)) -- prints 6 (longhand for above) | ||
print(#test) -- prints 3! | print(#test) -- prints 3! | ||
− | print(newTable {1,2,3, four = 4}:length()) -- believe it or not, this works too (prints 4)</ | + | print(newTable {1,2,3, four = 4}:length()) -- believe it or not, this works too (prints 4)</source> |
− | Methods are a nice way to make your code not only cleaner, but also more modular. Instead of having to repeat the same code over and over for multiple tables, you can just | + | Methods are a nice way to make your code not only cleaner, but also more modular. Instead of having to repeat the same code over and over for multiple tables, you can just <tt>__index</tt> them to the same methods table and they can all take advantage of methods. |
Revision as of 03:53, 27 September 2009
Lua |
---|
Basics |
Intermediate |
Advanced |
|
XLua |
Add to this template |
Methods are functions that are used by an object. 'Object' doesn't necessarily mean a game object, but instead some data structure that contains a set of variables and functions (the methods). Of course, the data structures we will be using are tables (usually).
Lua's methods have a special syntax; you don't need to use this syntax, but it's cleaner and easier than the alternative. Called colon syntax, it of course uses a colon instead of a dot:
-- regular function
table.function_call()
-- method
table:method_call()
Basically, by using the colon, you insert the data structure immediately left of the colon as a hidden first parameter. Therefore, these two calls are equivalent:
table.method(table, other_param)
table:method(other_param)
When you're writing the source code, however, that first parameter will generally be something else. It's just a regular variable, so it doesn't necessarily need to have the same name as the data structure calling the method (you should avoid this, in fact). You'll often see this variable named self, as this is the variable name Lua uses if the method definition does not explicitly state another name for its first parameter.
Table Methods
In order to use methods for any given table, we need to give it a metatable. Because we don't want the methods to actually "pollute" this table, we will have them stored in a separate table called methods. So our code should start out like this:
methods = { }
function newTable(t_data)
return setmetatable(t_data, methods)
end
Now we need to write a method for the methods table. A useful one will return the length of the entire table, including non-numeric keys, which are normally ignored by the length operator. So our methods table will look like:
methods = {
length = function (self)
local l = 0
for k in pairs(self) do
if string.sub(k, 1, 2) ~= "__" then
l = l + 1
end
end
return l
end,
}
methods.__index = methods
This is a similar table iteration as the __add metamethod from the Metatables tutorial. However, instead of adding the values at two tables' indexes, we're just incrementing a variable to use as our length. Note how we are using string.sub to exclude key names starting with '__'; we don't want metamethods to be counted in the length! We also define methods' __index metamethod to point to itself, so Lua knows this is the table that has the methods we need. We're actually done now, so we should try testing it:
methods = {
length = function (self)
local l = 0
for k in pairs(self) do
if string.sub(k, 1, 2) ~= "__" then
l = l + 1
end
end
return l
end,
}
methods.__index = methods
function newTable(t_data)
return setmetatable(t_data, methods)
end
test = newTable {1, 2, 3, a = 4, b = 5, c = 6}
print(test:length()) -- prints 6
print(test.length(test)) -- prints 6 (longhand for above)
print(#test) -- prints 3!
print(newTable {1,2,3, four = 4}:length()) -- believe it or not, this works too (prints 4)
Methods are a nice way to make your code not only cleaner, but also more modular. Instead of having to repeat the same code over and over for multiple tables, you can just __index them to the same methods table and they can all take advantage of methods.