Lua III

Metatables (Objects, sort of) & Review

Metatables

  • Metatables are one of the newest additions to Lua
    • Added in Lua 5.0
  • Metatables themselves are ordinary tables
    • They gain their special status through how we attach them to tables
  • Metatables can be used to represent user-defined types
  • All data types have metatables
    • Only the metatables of tables can be set and modified

Basic Metatable Use

In [1]:
Window = {}
Window.prototype = {x = 0, y = 0, width = 100, height = 100}
Window.mt = {}

function Window.new(o)
    setmetatable(o,Window.mt)
    return o
end

Window.mt.__index = Window.prototype

w = Window.new({x = 10, y= 20})
z = Window.new{height = 100, width = 50}
In [2]:
w.width
Out[2]:
100	
In [3]:
z.x
Out[3]:
0	

What can we do with Metatables

  • Set Default values
  • Limited Operator Overloading
    • Can only overload a small set of built in functions
    • No concept of method signature
  • Reuse a metatable for multiple tables, ie a class
  • Fake inheritance

Setting Default Values

  • To set default values when querying, we use the __index metamethod
    • What this actually does is provide a fall back table whose values are accses when the main table returns now for a given key
  • As tables can hold functions, this allows a way to do inheritance

Default Values Example

In [4]:
Building = {lat = 39.2555, long = 76.7113}
function Building.print(self)
    print("I'm located at " .. self.lat .. ", " .. self.long)
end
Building.__index = Building
UMBC = {}
setmetatable(UMBC,Building)

UMBC.print(UMBC)
Out[4]:
I'm located at 39.2555, 76.7113	

Inheritance Example

In [7]:
House = {numberOfBathrooms = 35}
function House.printBaths(self)
    print("I have " .. self.numberOfBathrooms .. " bathrooms.")
end

setmetatable(House,Building)
T = {}
T.__index = House
whiteHouse = {lat = 38.8977, long = 77.0336}
setmetatable(whiteHouse,T)--{__index = House})

whiteHouse.print(whiteHouse)
whiteHouse.printBaths(whiteHouse)
Out[7]:
I'm located at 38.8977, 77.0336	
I have 35 bathrooms.	
In [ ]:

Operator Overloading

  • To overload a given operator, set the corresponding metamethod
    • To overload +, define __add (two underscores)
    • Each metatable only has one metamethod of each name
    • To account for differing operand types, a long sequence of if statements may be needed

Operator Overloading Example

  • Suppose we have a metatable for the class of complex numbers
In [9]:
--Based off Fabio Mascarenhas' example
mt = {}
function mt.__add(c1,c2)
    if(getmetatable(c1) ~= mt) then
        res = {r = c1 + c2.r , i = c2.i}
        setmetatable(res,mt)
        return res
    end
    if(getmetatable(c2) ~= mt) then
        res = {r = c1.r + c2, i = c1.i}
        setmetatable(res,mt)
        return res
    end
    res = {r = c1.r + c2.r , i = c1.i + c2.i}
    setmetatable(res,mt)
    return res
end

complex = {r = 1, i = 2}
complex2 = {r = 2, i = 4}
setmetatable(complex,mt)
setmetatable(complex2,mt)
print(5 + complex) 
print(5 - complex)
Out[9]:
{
  r : 6
  i : 2
}
[string "--Based off Fabio Mascarenhas' example..."]:24: attempt to perform arithmetic on global 'complex' (a table value)
stack traceback:
	[string "--Based off Fabio Mascarenhas' example..."]:24: in main chunk
	[C]: in function 'xpcall'
	/home/bryan/torch/install/share/lua/5.1/itorch/main.lua:179: in function </home/bryan/torch/install/share/lua/5.1/itorch/main.lua:143>
	/home/bryan/torch/install/share/lua/5.1/lzmq/poller.lua:75: in function 'poll'
	/home/bryan/torch/install/share/lua/5.1/lzmq/impl/loop.lua:307: in function 'poll'
	/home/bryan/torch/install/share/lua/5.1/lzmq/impl/loop.lua:325: in function 'sleep_ex'
	/home/bryan/torch/install/share/lua/5.1/lzmq/impl/loop.lua:370: in function 'start'
	/home/bryan/torch/install/share/lua/5.1/itorch/main.lua:350: in main chunk
	[C]: in function 'require'
	(command line):1: in main chunk
	[C]: at 0x00406670
In [ ]:

OOP in Lua

  • We will discuss object oriented programming in detail over the next few classes
  • In Lua, we can approximate an object by
    • Using metatables to define a class
    • Use the : operator as a shorthand for passing the current value to a function

OOP in Lua Example

In [10]:
Animal = {species = 'Dog', noise = "woof"}

function Animal.makeNoise(animal)
    print(animal.species .. " goes " .. animal.noise)
end

Animal.makeNoise(Animal)
Out[10]:
Dog goes woof	
In [14]:
Animal2 = {species = "Dog" , noise = "woof"}

function Animal2:makeNoise(punct)
    print( self.species .. " goes " .. self.noise .. punct )
end

Animal2:makeNoise("!")
Animal2.makeNoise(Animal2,"!")
Out[14]:
Dog goes woof!	
Dog goes woof!	
In [12]:
Animal3 = {species = "Dog" , noise = "woof"}

function Animal3.makeNoise(self)
    print( self.species .. " goes " .. self.noise )
end

Animal3:makeNoise()
Out[12]:
Dog goes woof	

Issues with OOP in Lua

  • Can be cumbersome
  • An object is still just a table
  • Metatables can be changed
  • Consider the output of the following:
In [15]:
type(Animal3)
Out[15]:
table	
In [ ]:
Window = {}
Window.prototype = {x = 0, y = 0, width = 100, height = 100}
Window.mt = {}
Window.mt2 = {}

function Window.new(o)
    setmetatable(o,Window.mt)
    return o
end

Window.mt.__index = Window.prototype
Window.mt2.__index = {color = "red", shape = "circle"}
In [20]:
x = Window.new{height = 200}
print(x.x,x.y,x.width,x.height)
setmetatable(x,Window.mt2)
print(x.x,x.y,x.width,x.height)

x = {10, 20, 30, 40}
print(x[1])
Out[20]:
0	0	100	200	
nil	nil	nil	200	
10	

Lua Wrap Up and Analysis

  • To finish up our discussion of Lua, we are going to run through some things from chapters 5-10 of the textbook

Names, Binding, and Scope

  • Names
    • Case sensitivity?
      • Yes
    • Can reserved words be used?
      • No
    • What characters are allowed?
      • Starts with letters, can have underscores and numbers
  • Binding
    • When does type binding occur?
      • At assignment
  • Scope
    • What is the default scope of a variable?
      • Global

Data Types

  • What are the data types?
    • Number, Boolean, String, Table, Nil, Function, Threads and User Data
  • Can the user define their own data types?
    • Yes, its cumbersome
  • Arrays
    • Are subscripts checked?
      • No
    • Can we use slicing?
    • No
    • How well supported are multidimensional arrays?
    • Not Very Well
  • Are pointers accessible?
    • No
  • Is there type checking?
    • No

Expressions and Assignments

  • Does Lua have operator precedence?
    • Yes
  • Does Lua allow operator overloading?
    • To an Extent
  • How are type conversions done?
    • Implicitly
  • Does Lua have compound assignment operators (ie +=) ?
    • No

Control Structures

  • What is the syntax of an if statement?
    • if <> then <> end
  • Does Lua have a multiple-select structure (ie switch)?
    • No
  • What are Lua's counter controlled loops
    • for
  • What are Lua's logic controlled loops?
    • while, repeat

Subprograms

  • What is the function syntax in Lua
    • function name(<>) <> end
  • Does Lua allow functions to be passed as parameters of other functions?
    • Yes
  • Are functions in Lua type-checked
    • No
  • Can functions in Lua return more than one value?
    • Yes
  • Can function definitions be nested?
    • Yes
  • Does Lua allow closures?
    • Yes

Lua Example Application

  • A common task in natural language processing is to be able to read a file and calculate various statistics on the words in that file
  • As an in-class exercise, we will write together a program that does the following
    • Reads in a text file and breaks it into words, based on spacing.
    • Counts the frequency of each word
    • Prints a the most common words in the file

Lua Example Application

Step 1: Read in the file

In [2]:
words = {}
f = io.open("words.txt")
for line in f:lines() do
    for word in string.gmatch(line,"%w+") do
        table.insert(words,string.lower(word))
        --print(word)
    end
end

Lua Example Application

Step 2: Count the words

In [10]:
counts = {}
for _,word in pairs(words) do
    counts[word] = 0
    --print(word)
end

for _,word in pairs(words) do
    counts[word] = counts[word] + 1
    --print(word)
end

Lua Example Application

Step 3: Print Most Common

In [9]:
together = {}
for i,w in pairs(counts) do
    table.insert(together,{word = i, count = w})
end

table.sort(together,function(a,b) return a.count > b.count end)
for i,w in ipairs(together) do 
    print(w.word, w.count)
end
Out[9]:
you	7	
to	7	
of	7	
know	5	
i	5	
in	5	
the	5	
it	4	
a	4	
is	4	
this	3	
are	3	
which	3	
by	3	
we	2	
have	2	
how	2	
mr	2	
holmes	2	
sherlock	2	
but	2	
him	2	
and	2	
can	2	
me	2	
that	2	
who	2	
my	2	
matter	2	
meet	1	
better	1	
am	1	
man	1	
alpha	1	
outstretched	1	
whom	1	
hailed	1	
said	1	
hardly	1	
four	1	
sir	1	
fingers	1	
other	1	
turn	1	
cried	1	
with	1	
what	1	
excuse	1	
longed	1	
tell	1	
market	1	
his	1	
member	1	
quivering	1	
business	1	
oh	1	
salesman	1	
name	1	
geese	1	
breckinridge	1	
club	1	
sold	1	
road	1	
mrs	1	
brixton	1	
some	1	
wheeler	1	
oakshott	1	
was	1	
could	1	
nothing	1	
interested	1	
windigate	1	
he	1	
go	1	
henry	1	
everything	1	
before	1	
pray	1	
discuss	1	
baker	1	
passing	1	
people	1	
swept	1	
had	1	
farther	1	
named	1	
very	1	
room	1	
endeavouring	1	
pleasure	1	
t	1	
case	1	
hands	1	
anything	1	
little	1	
explain	1	
assisting	1	
don	1	
cosy	1	
trace	1	
fellow	1	
place	1	
rather	1	
wind	1	
than	1	
were	1