H. Conrad Cunningham
14 September 2016
Note: I corrected a few formatting errors on 29 January 2020.
Acknowledgements: These slides are adapted from the slide sets 0-5 for the course Programming in Lua created in July 2013 by Fabio Mascarenhas, Federal University of Rio de Janeiro, Brazil. It was presented at Nankai University, P. R. China, in July 2013. Although that course used Lua 5.2, I have attempted to update these slides to Lua 5.3. I produced these slided for CSci 450/503 (Organization of Programming Languages), Fall 2016.
Advisory: The HTML version of this document may require use of a browser that supports the display of MathML. A good choice as of January 2020 is a recent version of Firefox from Mozilla.
Portability
Simplicity
Embeddability
Efficiency
REPL (Read-Evaluate-Print-Loop)
Supports interactive experimentation
Execute REPL – run without script file argument
lua
Statement is basic level – assignment, conditional, loop, function call
Chunk is sequence of statements: unit of variable scoping
No statement separators required
> a = 1 b = 2 print(a, b)
1 2
But using semicolon statement separator aids readability
> a = 1; b = 2; print(a, b)
1 2
Better to put multiple-line input into file (a script)
Example file defs.lua
containing factorial function
function fact(n)
if n < 2 then
return 1
else
return n * fact(n-1)
end
end
Load file in REPL with dofile
function
> dofile("defs.lua")
> print(fact(5))
120
print
and dofile
built-in functions
Function calls mostly familiar, with some unusual features
Most built-in functions in different namespaces – math.sin
, math.pi
, math.sqrt
> print(math.sin(math.pi/ 3))
0.86602540378444
> print(math.sqrt(3) / 2)
0.86602540378444
Lua variables are
local
, addressed later)nil
Example
> print(x)
nil
> x = 2
> print(x)
2
Consist of sequences of letters, digits, & underscores not beginning with digit
Include _
(one underscore) as dummy variable assigned but not accessed
Reserved for Lua if begin with _
followed by all uppercase (e.g. _VERSION
, _ENV
, _G
)
Cannot include reserved words
and break do else elseif end
false for function goto if in
local nil not or repeat return
then true until while
Which of the following names are valid variable names? Why/why not?
---
_end
End
end
until?
Nil
NULL
Which of the following names are valid variable names? Why/why not?
---
– NO – a comment_end
– YESEnd
– Yesend
– No – reserved keyworduntil?
– No – ?
not validNil
– YesNULL
– Yesnil
– only value is nil
boolean
– values true
and false
number
string
– immutable byte vectors, any byte values, in whatever codingtable
– associative arraysfunction
– both named and anonymoususerdata
– opaque blobs handled by external (e.g., C) librariesthread
– really coroutineVariable does not have static type
Variable’s value has type
Assignment may change type of variable’s value
Function type
queries value, returns type as string
Value nil
denotes absence of useful value
nil
is value of
Most operations on nil are errors
Relational operators produce boolean
value true
or false
But any value usable in condition or with logical operators
false
and nil
are false – called falsey0
and ""
are truthyand
operator yields first operand if falsey; otherwise second
or
operator yields first argument if truthy; otherwise second
not
operator always yields true
or false
For optional parameter p
of a function, use p or d
where d
is the default value
function greeting(s)
s = s or "Hello"
print(s .. ", World!")
end
greeting() -- "Hello, World!"
greeting("Goodbye") -- "Goodbye, World!"
For ternary operator like a ? b : c
in C, use a and b or c
Note: not
binds more tightly than and
, and
binds more tightly than or
function max(a, b)
return (a > b) and a or b
end
Represented by IEEE double precision floating point
Has no integer type, but double precision can precisely represent integers up to 53 bits
Uses C and Java-like numeric literals, including scientific and hexadecimal (with “0x
”) notation
Supports usual arithmetic and relational operators: +
, -
, *
, /
, ^
(exponentiation), %
(modulo), <
, >
, <=
, >=
, ==
, and ~=
(not equal)
Careful! division by zero is not error
Added alternative representation for type number
: 64-bit integers
Sought minimal changes to behavior
Added floor division operator //
for integers
Added math
library functions maxinteger
, mininteger
Added math.type
function to distinguish integer
from float
Lua string
is immutable sequence of bytes
But what about text?
Built-in operations on strings
..
), length (#
)<
, >
, <=
, >=
use lexicographic orderingMore operations in string
library, other libraries
Use either single or double quotes
Can also use escape sequences
\a
(bell), \b
(backspace), \f
(form feed),\n
(newline), \r
(carriage return), \t
(tab), \v
(vertical tab), \\
(backslash), \"
(double quote), \'
(single quote)\ddd
(byte with decimal value ddd
), \xhh
(byte with hexadecimal value hh
), \z
(skips all spaces following it)Use delimiters [[
and ]]
Do not interpret escape sequences
Can extend for several lines; first character discarded if newline
Can use other delimiters: [=[
and ]=]
, [==[
and ]==]
, [===[
and ]===]
, etc.
escapes = [[
\f (formfeed)
\n (newline)
\r (carriage return)
]]
print("|" .. escapes .. "|")
Represent associative arrays – map key to value
nil
Can build flexible data structures
Used pervasively, everything is a table, more later
Are first-class values: can store functions in variables, pass as arguments, return as results
Can take variable number of parameters
Can return multiple values (unusual feature!)
Implemented with tail call optimization
Used pervasively, more later
How can you check whether a value is a boolean without using the type
function?
How can you check whether a value is a boolean without using the type
function?
function isbool(x)
return (not not x) == x
end
if-then-else
if
statement executes then
chunk if condition is truthy
if
statement executes optional else
chunk if condition is falsey or does nothing if else
omitted
if a < 0 then
print("a is negative")
a = -a
else
print("a is positive")
end
elseif
Simplify structure using elseif
clauses instead of nested if
statements
if op == "+" then
r = a + b
elseif op == "-" then
r = a - b
elseif op == "*" then
r = a * b
elseif op == "/" then
r = a / b
else
error("invalid operation")
end
else
clause remains optional
while
and repeat
while
loop executes body chunk while condition truthy – tests at beginning
i = 1; sum = 0
while i <= 5 do
sum = sum + (2 * i - 1)
i = i + 1
end
print(sum)
repeat
loop executes body chunk until condition truthy – tests at end
i = 1; sum = 0
repeat
sum = sum + (2 * i-1)
i = i + 1
until i > 5
print(sum)
for
(1)Numeric for
loop steps control variable by 1 from starting to ending argument, executes body chunk for each value
sum = 0
for i = 1, 5 do
sum = sum + (2 * i - 1)
end
print(sum)
Control variable local to body, not known after loop
Cannot assign to control variable
Can use break
statement to terminate loop early
for
(2)Optional third argument to numeric for
gives step value (1 if omitted)
sum = 0
for i = 5, 1, -1 do
sum = sum + (2 * i - 1)
end
print(sum)
Expressions for starting, ending, and step values evaluated only once before loop
for
loop executes zero times if starting value greater than ending (or lesser for negative step)
local
statement declares new variable visible from next statement to end of current chunk
local sum = 0 -- local to the program
for i = 1, 5 do
local n = 2 * i - 1 -- local to the for body
sum = sum + n
end
print(sum, n) -- problem?!
New local variables shadow global or local variables with same name
Use local variables whenever possible
Common idiom: cache value of global in local variable with same name
do-end
Entering code from previous slide in REPL does not do what expected – each statement separate chunk
do-end
statement introduces new scope without changing control flow
sum = 0
do
local i = 1
while i <= 5 do
sum = sum + (2 * i - 1)
i = i + 1
end
end
print(sum)
Can assign to several different variables in single step
> a, b = 10, 2 * sum
> print(a, b)
10 50
First evaluate all expressions on right side, then do assignments
> a, b = b, a
> print(a, b)
50 10
If more variables than values to assign, assign nil
to extra
> a, b, sum = 10, 2 * sum
> print(a, b, sum)
10 50 nil
If more values than variables, ignore extra values
Multiple assignments useful with functions that return multiple values
local
statement can declare and initialize several local variables with multiple assignment form
local i, j, k = 1, 2, 3
What is the result of running the following program? Why?
local i = 5
while i do
print(i)
i = i - 1
end
What is the result of running the following program? Why?
local i = 5
while i do
print(i)
i = i - 1
end
Printing integers counting down from 5 – infinitely!
Function abstracts and parameterizes chunk
Function call either statement or expression
a statement if executed for side effects only
> print(8*9, 9/8)
72 1.125
an expression if returned value used
> x = math.sin(3) + math.cos(10)
> print(x, os.date())
-0.69795152101659 06/23/13 14:49:03
Function has both side effects and return values
Good style: Best to avoid (impure) functions that have side effects
Functions are first-class values
Function names are just variables (in same namespace)
Be careful with shadowing of function names
> print(print)
function: 0000000068B94B40
> do
>> local print = 10
>> print(print)
>> end
stdin:3: attempt to call local 'print' (a number value)
stack traceback:
stdin:3: in main chunk
[C]: in ?
local
functions local to chunk, just like local variables
local function max(a, b)
return (a > b) and a or b
end
Function body is chunk
Function parameter local variable in body
return
statement returns control and values
Functions can omit local
function max(a, b)
return (a > b) and a or b
end
If existing variable named max
, then above function becomes new value
If no variable named max
, then above function becomes value of new global
Following equivalent to local
definition on previous slide:
local max
function max(a, b)
return (a > b) and a or b
end
Functions can be anonymous, defined in expression
local max = function (a, b)
return (a > b) and a or b
end
Actually all Lua functions are anonymous; other style just syntactic sugar
Style note: Better to use function definition statements
Functions can return multiple values
Useful with multiple assignments or multi-argument function calls
> s, e = string.find("hello Lua users", "Lua")
> print(s, e)
7 9
> print(string.find("hello Lua users", "Lua"))
7 9
List all return values after return
function maxmin(a, b)
if a < b then
return b, a
else
return a, b
end
end
If function call last in list of expressions, append all results
> a, b = maxmin(2, 3)
> print(a, b)
3 2
> a, b, c = 1, maxmin(2, 3)
> print(a, b, c)
1 3 2
> print("maxmin(2, 3)", maxmin(2, 3))
maxmin(2, 3) 3 2
> function f(x) return maxmin(x, 0) end
> print(f(-4))
0 -4
Otherwise use just first result, ignore rest
If no results, use nil
Using just first result
> a, b = maxmin(2, 3), 5
> print(a, b)
3 5
> print(maxmin(2, 3), 5)
3 5
> print(maxmin(2, 3) + 2)
5
Enclosing function call in parenthesis forces just one result
> a, b = (maxmin(2,3))
> print(a, b)
3 nil
> print((maxmin(2,3)))
3
Last parameter of function can be special token ...
All arguments passed to call from position of ...
forward referenced by ...
inside body
> function id(...) return ... end
> print(id(1, 2, 3))
1 2 3
function printf(fmt, ...)
io.write(string.format(fmt, ...))
end
> printf("%s(%d, %d)\n", "maxmin", 2, 3)
maxmin(2, 3)
What is the output of the following program?
local function range(a, b, c)
if a > b then
return
else
return a, range(a + c, b, c)
end
end
print(range(1, 9, 2))
What is the output of the following program?
local function range(a, b, c)
if a > b then
return
else
return a, range(a + c, b, c)
end
end
print(range(1, 9, 2))
>1 3 5 7 9
Tables only way to structure data in pure Lua
Can efficiently represent arrays, sets, records, objects, etc.
Basic operations
{}
– construciton to make new table
[]
– indexing to read/write values
> tab = {} -- make a new table assign to tab
> tab["x"] = 5 -- associate 5 with string key `"x"`
> print(tab["x"]) -- read value of key `"x"` entry and print it
Table is mutable reference type – careful of “pointer” aliasing (as with Java object references)
> alias = tab
> alias["x"] = "changed"
> print(tab["x"])
changed
Table with values assigned to integer keys sequentially from 1
local a = {}
for i= 1, 6 do
a[i] = math.random(10)
end
Initialize with list in table constructor
local a = { math.random(10), math.random(10), math.random(10),
math.random(10), math.random(10), math.random(10) }
Do not allow value nil
– array cannot have holes but can be filled in any order
Length operator #
gives number of elements in array
Use #
to iterate over array with for
loop
local a = { math.random(10), math.random(10), math.random(10),
math.random(10), math.random(10), math.random(10) }
for i= 1, #a do
print(a[i])
end
Common idiom: Use #
to add/remove elements at end
a[#a] = nil -- remove the last element
a[#a + 1] = math.random(10) -- add a new element to the end
Function table.insert
inserts element at any position – shifts right
> a = { 1, 2, 3, 4, 5 }
> table.insert(a, 3, 10) -- insert 10 in position 3
> print_array(a)
{ 1, 2, 10, 3, 4, 5 }
Function table.remove
deletes element from any position – shifts left
> table.remove(a, 4) -- remove fourth element
> print_array(a)
{ 1, 2, 10, 4, 5 }
Function table.sort
sorts elements efficiently
> a = { "Python", "Lua", "C", "JavaScript", "Java", "Lisp" }
> table.sort(a)
> print_array(a)
{ C, Java, JavaScript, Lisp, Lua, Python }
Function table.concat
concatenates array of strings using optional separator
function print_array(a)
print("{" .. table.concat(a, ", ") .. "}")
end
If no separator then use ""
Common idiom: use array of strings as buffer, then concatenate
ipairs
Can iterate over array using generic for
loop and ipairs
function
local a = { 1, 3, 5, 7, 9 }
local sum = 0
for i, x in ipairs(a) do
print("adding element " .. i)
sum = sum + x
end
print("the sum is " .. sum)
Has two control variables – first gets indices, second gets elements
Often interested only in elements, so use _
as control variable for indices
Can represent multi-dimensional arrays with “jagged arrays” – arrays of arrays
local mt = {}
for i= 1, 3 do
mt[i] = {}
for j = 1, 5 do
mt[i][j] = 0
end
end
Alternatively can use multiplication to compute index directly
local mt = {}
for i= 1, 3 do
for j = 1, 5 do
mt[(i-1)*5+j] = 0
end
end
Table with string keys that are valid Lua identifiers
point1 = { x = 10, y = 20 }
point2 = { x = 50, y = 5 }
line = { from = point1, to = point2, color = "blue" }
Read and write fields with the .
operator
line.color = "red" -- same as line["color"] = "red"
print(line.from.x, line["color"])
No inherent order among keys
Table can be both record and array – initialize both in single constructor
Represent set with Table where keys are the elements, values are true
(disallows nil
as set member)
Can test membership and add/remove elements by indexing
Initialize using third table constructor syntax [
expression]
-- a set of four random integers from 1 to 10
local set = { [math.random(10)] = true, [math.random(10)] = true,
[math.random(10)] = true, [math.random(10)] = true }
Replace true
by number of occurrences to get bag (or multiset) data structure
pairs
Can iterate over table with generic for
using pairs
function to get all key-value pairs
local tab = { x = 5, y = 3, 10, "foo" }
for k, v in pairs(tab) do
print(tostring(k) .. " = " .. tostring(v))
end
First control variable gets keys, second gets values
Function pairs
does not guarantee order, even using numeric keys; ipairs
does use order for arrays
Function pairs
works well for sets using _
to ignore control variable for values
What will be the output of following program?
sunday = "monday" ; monday = "sunday"
t = { sunday = "monday", [sunday] = monday }
print(t.sunday, t[sunday], t[t.sunday])
sunday = "monday" ; monday = "sunday"
t = { sunday = "monday", [sunday] = monday }
print(t.sunday, t[sunday], t[t.sunday])
Second line same as:
t = {} ; t["sunday"] = "monday" ; t["monday"] = "sunday"
Last? Variable sunday
yields "monday"
. So t[sunday]
same as t["monday"]
. Variable monday
yields "sunday"
. Thus assignment is t["monday"] = "sunday"
.
Thus t == { sunday = "monday", monday = "sunday" }
. So print(t.sunday, t[sunday], t[t.sunday])
:
monday sunday sunday
Comments
End-of-line comments begin with
--
(two hyphens)Block comments begin with
--[[
and end with]]
---[[
starts an end-of-line comment, not block (Useful for turning off block comments)