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
luaStatement 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
endLoad file in REPL with dofile function
> dofile("defs.lua")
> print(fact(5))
120print 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.86602540378444Lua variables are
local, addressed later)nilExample
> print(x)
nil
> x = 2
> print(x)
2Consist 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 whileWhich of the following names are valid variable names? Why/why not?
---_endEndenduntil?NilNULLWhich 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 nilboolean – values true and falsenumber
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
endRepresented 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
nilCan 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-elseif 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")
endelseifSimplify 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")
endelse clause remains optional
while and repeatwhile 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-endEntering 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 50First evaluate all expressions on right side, then do assignments
> a, b = b, a
> print(a, b)
50 10If 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, 3What 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
endFunction 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
endActually 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 9List all return values after return
function maxmin(a, b)
if a < b then
return b, a
else
return a, b
end
endIf 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)
5Enclosing function call in parenthesis forces just one result
> a, b = (maxmin(2,3))
> print(a, b)
3 nil
> print((maxmin(2,3)))
3Last 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 itTable is mutable reference type – careful of “pointer” aliasing (as with Java object references)
> alias = tab
> alias["x"] = "changed"
> print(tab["x"])
changedTable 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
ipairsCan 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
pairsCan 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)