Lua can be started from the command-line and used as a calculator:
$> lua53
Lua 5.3.0 Copyright (C) 1994-2014 Lua.org, PUC-Rio
> 2*5 + 3
13
> 2*(5 + 2)
14
> 5^2 + 1
26.0
Regular numbers are the simplest kind of value, and can be combined into
expressions using operators. +
,*
and ^
(meaning 'to the power of') are common
arithmetic operators.
It would not be a very good scientific calculator if the standard mathematical functions were not available:
> math.sin(0.4) + 1
1.3894183423087
This function takes a number value, and calculates the sine of that number in radians. The conversion from degrees is straightforward, so we will calculate it and give that value a name:
> d2r = math.pi/180
> math.sin(90*d2r)
1.0
d2r
is called a variable, and giving it a value is called assignment. You
should not pronounce =
"equals" but rather call it "becomes" or something like that; it
does not mean checking for equality! This is where standard programming notation
is different from mathematics. Otherwise this would make no sense:
> x = 1
> x = x + 1
> x
2
Functions are a kind of value as well, so you can define shortcuts:
> s = math.sin
> c = math.cos
> s(x)^2 + c(x)^2
1.0
Comparisons between values involve another kind of expression involving
conditional operators. Note ==
here does mean 'test for equality':
> 10 > 12
false
> 42 == 42
true
> 5 < 7
true
These are often called 'boolean' operators after the English logician George Boole, and the resulting value is called 'boolean' in Lua.
Arrays are an important kind of value in programming languages. They store
a sequence of values. They have a size which is returned by the #
operator,
and can be indexed using a integer value:
> arr = {10,20,30}
> #arr
3
> arr[1]
10
> arr[3]
30
> arr[20]
nil
Note that Lua arrays go from 1 to the length of the array. It is not an error
to index outside that range - you will simply get nil
. This is a special value
meaning 'no value'. This is also the value of an undefined variable:
> frodo
nil
Maps (often called associative arrays or lookup tables) are also indexed but by arbitrary values. They consist of key/value pairs.
> map = {one=1,two=2,three=3}
> map['one']
1
> map.one
1
> map.four
nil
> map.four = 4
> map.four
4
Note an important feature of Lua maps - you can use []
as before (except with
strings in this case) but map.key
is exactly the same as map['key']
. And if
the key is not present in the map, then the result is (again) nil
.
In Lua, both maps and arrays are called tables. We used math.sin
to calculate
the sine of a number, and math
is just a table of functions. Generally
you would use a table either as an array or a map, but they can be mixed.
Tables are a distinct type, which is the proper term for 'kind of value'. It is an error to pass the wrong type to a function expecting a number:
> math.sin(map)
stdin:1: bad argument #1 to 'sin' (number expected, got table)
stack traceback:
[C]: in function 'math.sin'
stdin:1: in main chunk
[C]: in ?
Strings are quoted text like "hello". You can quote with either double or single
quotes, Lua does not care. The length operator #
will give the size in bytes:
> s = 'hello'
> #s
5
By byte I mean a 8-bit value (the smallest addressable unit of memory); for ASCII text, a byte is a character, but in general a character may be several bytes. Unlike tables, you cannot index a string. You cannot modify a string either, it is said that they are immutable.
Strings are a distinct type of value, and so you get a type error when using
them in the wrong way. But if they represent a number, then tonumber
will convert
that string into a number.
> math.sin(s)
stdin:1: bad argument #1 to 'sin' (number expected, got string)
...
> s = "42"
> tonumber(s)
42
Like real-world strings, they can be combined or concatenated using ..
. Or
you can join a table of strings and numbers together using table.concat
:
> hello = "hello"
> world = "world"
> hello.." "..world
hello world
> table.concat({"hello","world",42},' ')
hello world 42
You can extract a part of a string using string.sub
:
> string.sub(hello,1,3)
hel
The original string is not changed in any way, but a 'slice' is copied to a new string.
Up to now, Lua is a convenient calculator. The concepts of value, expression, variable and assignment are important programming concepts, but we haven't started programming yet!
- defining our own functions
- doing different things based on some condition
- repeating things until some condition is true
Now it's time to introduce statements. An expression always has a value; an
assignment like x = 2
is a statement and has no value.
To conditionally perform some action, use the if
statement:
> x = 2
> if x > 1 then print("ok") end
ok
These statements are generally if EXPRESSION then STATEMENTS end
. The words
if
,then
and end
are keywords, and these are reserved words in Lua. You
cannot use keywords for variables.
The if
statement can also have an else
clause:
> if x > 1 then print("ok") else print("nope") end
To repeat things with a range of values, use the for
statement:
> for i = 1,5 do print(i) end
1
2
3
4
5
At this point, it's time to start writing programs. Type this into a text editor:
-- a comment, ignored by Lua. This is 'hello.lua'
for i = 1,5 do
print('hello',i)
end
and then
$> lua53 hello.lua
hello 1
hello 2
hello 3
hello 4
hello 5
It's important to format code nicely, because it makes it easier to read. Anything
between the do
and the end
should be indented. Tabs, spaces, it doesn't matter: just
be consistent. Use a text editor
that understands this (I'm personally a fan of Geany).
Can now add an if
statement:
-- goodbye.lua
for i = 1,5 do
if i > 2 then
print('goodbye',i)
else
print('hello',i)
end
end
which gives us:
hello 1
hello 2
goodbye 3
goodbye 4
goodbye 5
Again, indent each statement properly. You could put this all on one line but people will hate you.
Every Lua program has a predefined table called 'arg' containing the command-line arguments passed to the program:
$> cat args.lua
-- args.lua
for i = 1,#arg do
print(i,arg[i])
end
$> lua53 args.lua one 42 'hello dolly'
1 one
2 42
3 hello dolly
You can use tonumber
to convert the first argument to a number. We will use
io.write
which works like print
except it doesn't end the line:
$> cat for.lua
-- for.lua
n = tonumber(arg[1])
for i = 1,n do
io.write('hi ')
end
print()
$> lua53 for.lua 5
hi hi hi hi hi
There's another function in the io
table which allows us to easily loop over
all the lines in a file. We assign 1 to i
, and then increment i
by one each time.
-- lines.lua
file = arg[1]
i = 1
for line in io.lines(file) do
print(i,line)
i = i + 1
end
And the output:
$> lua53 lines.lua lines.lua
1 -- lines.lua
2 file = arg[1]
3 i = 1
4 for line in io.lines(file) do
5 print(i,line)
6 i = i + 1
7 end
This is other form of the for
statement, which works with iterators.
Here's another iterator function pairs
. It gives all the key/value pairs in
a table - in this case the predefined table math
:
> for k,v in pairs(math) do print(k,v) end
min function: 0x41e1d0
tan function: 0x41dfb0
modf function: 0x41e5d0
maxinteger 9223372036854775807
asin function: 0x41e4a0
ceil function: 0x41e580
rad function: 0x41df50
random function: 0x41e080
mininteger -9223372036854775808
floor function: 0x41e690
huge inf
max function: 0x41e260
sqrt function: 0x41dfe0
pi 3.1415926535898
tointeger function: 0x41e6e0
atan function: 0x41e440
abs function: 0x41e760
sin function: 0x41e020
acos function: 0x41e4d0
randomseed function: 0x41e050
log function: 0x41e2f0
ult function: 0x41e3a0
cos function: 0x41e410
fmod function: 0x41e7d0
type function: 0x41e500
deg function: 0x41df80
exp function: 0x41e3e0
(For more information, go to Mathematical Functions in the Lua manual.)
In mathematics, simple functions take a value from a set of all possible inputs, the domain and output a value from a set called its range. Or, we apply a function to any value from the domain and get a value from the range. So the simplest form of 'sine' goes from all real numbers to the range (-1,+1).
In programming we call a function and pass it an argument. It will then return a value. These words may seem strange (they are not how we talk about mathematical functions) and come from the early days of programming, when 'calling' a 'subprogram' involved saving your position in memory and jumping to a new position. The subprogram would do its work, and then would 'return' to the saved original position.
The keyord function
defines a function in Lua; it is followed by the name,
and then the names of arguments. Like for
and if
, any number
of statements may appear afterwards ending with end
. The return
statement
passes the value back explicitly:
-- sqr.lua
function sqr(x)
print('x is',x)
return x * x
end
y = sqr(10)
print('y is',y)
print('x is',x)
-- output
-- x is 10
-- y is 100
-- x is nil
The value 10 is assigned to the variable x
in the function sqr
, and we get
back 100.
Note that x
is nil
outside the function! That's to say, it's undefined.
Consider these lines in the Lua Prompt:
> sum = 0
> for i = 1,50 do sum = sum + i end
> sum
1275
> = i
nil
Again, i
is only defined inside the loop, between the do
and end
.
These are examples of local variables, which are only visible in a particular part of a program; global variables are visible everywhere. Life without local variables would be a mess, honestly. Consider this more readable version:
function sum_numbers (n)
sum = 0
for i = 1,n do
sum = sum + i
end
return sum
end
It does the job, sure, but every time we call it, it updates a global variable
called sum
. Now there are only so many meaningful names you can give to
variables (without stupid_long_names_creating_confusion
) and you do not want
the insides of functions 'escaping' like this!
Setting the global variable sum
is a side effect and is
something to be avoided.
So this is better:
function sum_numbers (n)
local sum = 0
for i = 1,n do
sum = sum + i
end
return sum
end
The keyword local
declares a variable, and usually also gives it an initial
value. sum
is now local to sum_numbers
(like the argument n
) and we have
a better behaved function. I encourage you to use local
declarations as much
as possible, even in little programs.
Lua functions may have more than one argument, like print
- it can take any
number of arguments. Lua functions may also return more than one value. Say
we want the position of some text within a larger body of text - or (properly)
the position of a substring in a string:
> s = "hello dolly"
> string.find(s,"doll")
7 10
> string.sub(s,7,10)
doll
string.find
returns two values: the start and the end position. You can
take these positions and get the substring back using string.sub
. The
end position is just after the end of the match; all positions are from one.
A common task is to find if a string starts with another string, which we can now
define as
function starts_with(str,sub)
return string.find(str,sub) == 0
end
local text = 'hello dolly you're so fine'
local hello = 'hello'
if starts_with(text,hello) then
print('goodbye')
end
In this comparison, the second return value is ignored.
Here is a function that returns both the minimum and the maximum value of a table of values:
-- minmax.lua
function minmax(values)
local vmin = math.huge
local vmax = -math.huge
for i = 1,#values do
vmin = math.min(vmin,values[i])
vmax = math.max(vmax,values[i])
end
return vmin, vmax
end
local values = {10,2,5,20,3,12}
local min,max = minmax(values)
print(min, max)
-- 2 20
I've used the constant math.huge
, which is guaranteed to be larger than any
other number, and also the min
and max
functions. Please note how we
can assign multiple values to several variables at once. This also means
that you can say local x, y = 1.0, 2.0
.
There are actually two variables called values
in this program, one local
to the function and the other to the so-called main chunk. They are completely
unrelated.
There's no built-in way to print out tables in Lua, but it's easy to write a function that will turn an array of strings and numbers into a human-readable string:
-- show.lua
function showa (arr)
return '{'..table.concat(arr,',')..'}'
end
And then you can load it into the Lua prompt using the 'l' (for 'load') flag. Note that here we don't use the extension '.lua'!
~/lua/luabuild$ lua53 -l show
Lua 5.3.0 Copyright (C) 1994-2014 Lua.org, PUC-Rio
> arr = {10,20,30,40}
> showa(arr)
{10,20,30,40}
>
We can now demonstrate some common operations on tables. We can insert a new value
into a table using a position (which starts from one); values can be removed.
There is a second form of table.insert
which takes two arguments, not three - it
will append the value to the end of the table.
> table.insert(arr,1,11)
> showa(arr)
{11,10,20,30,40}
> table.remove(arr,2)
10
> showa(arr)
{11,20,30,40}
> table.insert(arr,50)
> showa(arr)
{11,20,30,40,50}
You can also create an array-like table by setting the values using an index:
> seq = {}
> for i = 1,10 do
>> seq[i] = i*i
>> end
> showa(seq)
{1,4,9,16,25,36,49,64,81,100}
> #seq
10
> seq[#seq + 1] = 200
> showa(seq)
{1,4,9,16,25,36,49,64,81,100,200}
There is no builtin way to find things in arrays either, but it's easy to write.
Just add the following function to show.lua
:
function finda (arr, value)
for i = 1,#arr do
if arr[i] == value then
return i
end
end
return nil
end
It's perfectly fine to return early from a function - we've found our position, let's
return what we found. If we do not find the value, then at the end of the for
loop we return nil
explicitly.
We have to restart with lua53 -l show
after this change:
> arr = {10,20,30,40,50}
> finda(arr,30)
3
> finda(arr,60)
nil
> if finda(arr,70) then
>> print('found')
>> else
>> print('not found')
>> end
not found
Note that we can directly test whether finda
returned nil
or not. The only two
values that are considered 'false' are the boolean false
and the value nil
.
And that's why I returned nil
, rather than some out-of-range number like 0.