Download presentation
Presentation is loading. Please wait.
Published byKade Corn Modified over 9 years ago
1
References: 1. “Programming in LUA, 2 nd ed.”, Roberto Lerusalmschy Chapters 13+16 (primarily) 2. “Lua Reference Manual” (included in Lua installation or online)
2
Overview Lua doesn't have a separate OOP facility. It does have means to "coerce" tables into classes. Not as pretty as python… We'll need to look at Metatables (ch. 13) We'll also look at (the hard way of) creating modules Why the hard way? A: It's how we'll have to do it in ssuge.
3
Normal "missing" behavior If Lua can't find a variable, it returns nil A global variable A member of a table [sample2_01.lua] x = 10 a = {x = 13, y = 15} print(x) -- 10 print(a.x) -- 13 print(a["x"])-- 13 print(y) -- nil print(a.y) -- 15 print(a.z) -- nil print(a["z"])-- nil
4
Functions and Tables Functions are "first-class" citizens in Lua. You can store them in variables. Normal variables or table entries. [Sample2_01.lua, cont.] function foo(test) print("Hello! You gave me '".. test.. "'") end bar = foo foo("blah") -- “Hello! You gave me ‘blah’” bar("argh") -- “Hello! You game me ‘argh’” a.func_member = foo a.func_member("joust!") --or... a = {x = 13, y = 15, func_member = function() -- (as before – end } a.func_member("yow") -- “Hello! You gave me ‘yow’”
5
MetaTables A metatable is: A normal Lua table Can be set as the metatable for another table (or sometimes itself, oddly enough…) ○ getmetatable(tab) ○ setmetatable(tab, meta_tab) Can contain metamethods (which sometimes can be implemented as tables) ○ __index is a special one: Called when a requested key is missing Can be a table or a function (with the table and key as parameters)
6
MetaTables (and __index) example (sample2_02.lua) a = {x = 9, y = 13, z = 15} print(a.x) -- 9 print(a.w) -- nil print(getmetatable(a)) -- nil m = {} -- going to be a metatable m.__index = {w = 1000, q = 20000} setmetatable(a, m) print(m) -- table: 0042EE08 print(getmetatable(a)) -- table: 0042EE08 print(a.x) -- 9 print(a.w) -- 1000 print(a.q) -- 20000 print(a.b) -- nil print(rawget(a, "x")) -- 9 print(rawget(a, "w")) -- nil ( Bypasses __index ) m.__index = function(tab, key) if key == "w" then return 1000 else if key == "q" then return 20000 end
7
Other "meta-methods" m.__add = function(t1, t2) if getmetatable(t1) ~= getmetatable(t2) then error("You can only call this method with two m-based tabls") end r = {} -- a new instance setmetatable(r, getmetatable(t1)) -- make result of same "type" as t1 for k, v in pairs(t1) do if t2[k] ~= nil then r[k] = t1[k] + t2[k] end return r end a = {x = 5, y = 9, z = 2} b = {x = 15, z = 3} setmetatable(a, m) setmetatable(b, m) c = a + b -- c is now {x = 20, z = 5} (with a metatable of m)
8
Other "meta-methods", cont. m.__tostring = function(t1) s = "[m-table| " -- Count number of key-value pairs in t1 num_elem = 0 for k, v in pairs(t1) do num_elem = num_elem + 1 end -- Append elements to s count = 0 for k, v in pairs(t1) do if count < num_elem - 1 then s = s.. v.. ", " else s = s.. v.. "]" end count = count + 1 end return s end a = {x = 15, y = 3, z = 5} setmetatable(a, m) print(a) -- [m-table| 15, 3, 5] (indirectly calls __tostring) x = tostring(a) -- x now holds “[m-table| 15, 3, 5]”
9
OOP in Lua We've actually already been using Lua's "OOP" facilities. Instances are tables We create another table (the class) and set the metatable of the instances. My example's pretty much that of the book's (Ch. 16)
10
OOP Example, part I/3 -- The "Class" definition Account = {balance = 0} -- A normal table. It's *going* to be the -- default attribute values for instances of the -- Account "class" function Account:withdraw(amt) self.balance = self.balance - amt end -- Alternate syntax #1 -- function Account.withdraw(self, amt) -- self.balance = self.balance – amt -- end -- Alternate syntax #2 -- Account = { balance = 0, -- function withdraw(self, amt) -- self.balance = self.balance – amt -- end -- }
11
OOP Example, part 2/3 -- The “constructor" function function Account:new(initialTable) -- self (in this context) is the Account class. -- Our job is to make a new table (the new object) -- of "class" Account. o = initialTable or {} -- sets o to a new table if nil is passed. setmetatable(o, self) -- Makes Account the metatable for o. self.__index = self -- Note: confusing! We just set o's metatable to -- be Account. If we define an __index method, -- o will look for any missing methods / keys in -- Account. This will also (later) play a role -- in inheritance. return o end -- like “__str__” in python. function Account:__tostring() return "[Account: balance=".. self.balance.. "]" end
12
OOP Example, part 3/3 -- Creating instances a = Account:new({balance=1000}) -- a = Account.new(Account, {balance=1000}) (alternate syntax) print(a) -- [Account: balance=1000] a:withdraw(200) print(a) -- [Account: balance=800] b = Account:new() -- passes nil for initialTable (so no values in table) b.balance = 200 -- does a lookup in Account.__index. Gets the value 0 and -- assigns to a *new* variable in b's table. That'll be used -- for b's balance in the future (Account.balance won't change) b:withdraw(100) print(a) -- [Account: balance=800] (making b didn’t change a) print(b) -- [Account: balance=100]
13
Inheritance Just a (clever / obfuscated) use of metatables and it's __index field. SpecialAccount = Account:new({limit=1000.0}) -- SpecialAccount is (for now) -- an instance of Account with a new field print(SpecialAccount) -- [Account: balance=0] s = SpecialAccount:new() -- Special Account doesn't have a new method. So... -- Lua looks in the metatable's __index table (which is -- Account) and calls its new method. Here’s the trick: -- self (in Account:new) is now SpecialAccount. So -- we’re setting __index calls to point there. But... -- if it doesn’t have that value, it uses its __index -- method (which points to Account). print(s) -- Interesting quirk: SpecialAccount doesn't have an __index method -- in it's metatable, so Lua defaults to the default-table output. -- it’s doing -- It doesn't dig deep enough. The easy solution... SpecialAccount.__tostring = Account.__tostring print(s) -- [Account: balance = 0]
14
Inheritance, cont. -- We can add methods to SpecialAccount just like any “class” function SpecialAccount:getLimit() if self.limit == nil then return 0 else return self.limit end
15
Inheritance and "polymorphism" -- If we add methods to SpecialAccount (which have the same name -- as one in Account), they will be used instead of Account's function SpecialAccount:withdraw(amt) if amt > self:getLimit() + self.balance then error("Insufficient funds!") else self.balance = self.balance - amt -- allow a (possibly) negative amt end t = SpecialAccount:new() u = SpecialAccount:new({balance = 150, limit = 900}) --t:withdraw(100) -- Would trigger the error u:withdraw(1000) print(u) -- [Account: balance=-850]
16
Modules in Lua A module is a table returned by the require statement Lua (and the OS) search according to LUA_PATH environment variable ?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua The “?” are replaced with “my_module” (here) All non-local “things” in the module are packaged into a table. -- This is on the “user” side local foo = require “my_module” -- foo contains one function called bar. x = foo.bar()
17
Modules, cont. There is a global variable package in Lua. package.path is the $PATH variable package.cpath is dll locations … package.loaded is all loaded modules When you use require the first time it adds to package.loaded. All future references to that module go through package.loaded (much faster).
18
Modules, cont. Finally, Lua keeps one more global variable, _G, which stores a reference to things in package.loaded also stores a reference to all global variables
19
Creating modules (the “disciplined” method) -- In “foo.lua” -- Part I: Create the module local modname =... --... is the name of this file (require is -- passing it to us as an argument) local M = {} -- Create a new table (the module) _G[modname] = M -- insert M into _G package.loaded[modname] = M -- insert M into package.loaded -- Part II: Make local references to any external “things” local sqrt = math.sqrt local table = table -- Part III: Freeze all external access (so we don’t clutter _G) setfenv(1, M) -- Changes our environment (the 1 indicates -- we’re changing this module’s environment. -- 2 would be the importer, 3 would be their -- importer, etc. -- Part IV: Write the module -- Anything we create here will go into M (and so the user will be able -- to do “foo.xyz” to access it). Note: If a normal Lua “thing” isn’t -- declared in part II, it will come back as a nil reference.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.