Difference between revisions of "Lua: Methods"

From Mario Fan Games Galaxy Wiki
 
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:
  
<nowiki>-- regular function
+
<source lang="lua" enclose="div">-- regular function
table.function()
+
table.function_call()
  
 
-- method
 
-- method
table:method()</nowiki>
+
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:
<nowiki>table.method(table, other_param)
+
<source lang="lua" enclose="div">table.method(table, other_param)
 
table:method(other_param)
 
table:method(other_param)
</nowiki>  
+
</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 'self', but there's nothing forcing you to call it that. However, this tutorial will use that as it's commonplace.
+
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 fact, we will make the table it's ''own'' metatable. This is as simple as taking the function from the Metatables tutorial and changing it a little. 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:
+
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:
<nowiki>methods = { }
+
<source lang="lua" enclose="div">methods = { }
  
function newTable(...)
+
function newTable(t_data)
     local t = ...
+
     return setmetatable(t_data, methods)
    setmetatable(t, t)
+
end</source>
    t.__index = methods
 
    return t
 
end</nowiki>
 
You'll notice how '''setmetatable''' has two 't' parameters: we have set the table to use itself as its metatable. You should also notice a new line, '''t.__index = methods'''. This is the ''index metamethod'', a table or function which is called when a lookup fails (because there is no entry there). Because a method lookup in one of our tables will always fail (we aren't storing the methods there, remember), '''__index''' tells Lua to look in the '''methods''' table for the requested index. As long as we give it a method that actually exists, it will find it just fine.
 
  
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:
+
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:
<nowiki>methods = {
+
<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,
}</nowiki>
+
}
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're actually done now, so we should try testing it:
+
methods.__index = methods</source>
<nowiki>methods = {
+
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 (...)
+
function newTable(t_data)
     local t = ...
+
     return setmetatable(t_data, methods)
    setmetatable(t, t)
 
    t.__index = methods
 
    return t
 
 
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)</nowiki>
+
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 '''__index''' them to the same methods table and they can all take advantage of methods.
+
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

 Standardwikimessagebox.png This page should be syntax highlighted

Code on this page would be more readable and may wrap correctly if it were surrounded by <source> tags using the enclose="div" attribute.

Lua
Lua.gif
Basics
Intermediate
Advanced
XLua
Add to this template
 Standardwikimessagebox.png This article assumes the use of Lua 5.1.

Information may not be accurate or may need revision if you are using a different version.

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.