Long strings
Exercise 2.6: Assume the following code:
a = {}; a.a = a
What would be the value of a.a.a.a? Is any a in that sequence somehow different from the others?
Now, add the next line to the previous code:
a.a.a.a = 3
What would be the value ofa.a.a.a now?
3
Expressions
Expressions denote values. Expressions in Lua include the numeric constants and string literals, variables, unary and binary operations, and function calls.
Expressions include also the unconventional function definitions and table con-structors.
3.1 Arithmetic Operators
Lua supports the usual arithmetic operators: the binary ‘+’ (addition), ‘-’ (sub-traction), ‘*’ (multiplication), ‘/’ (division), ‘^’ (exponentiation), ‘%’ (modulo), and the unary ‘-’ (negation). All of them operate on real numbers. For instance, x^0.5 computes the square root of x, while x^(-1/3) computes the inverse of its cubic root.
The following rule defines the modulo operator:
a % b == a - math.floor(a/b)*b
For integer operands, it has the usual meaning, with the result always having the same sign as the second argument. For real operands, it has some extra uses. For instance,x%1 is the fractional part of x, and so x-x%1 is its integer part. Similarly,x-x%0.01 is x with exactly two decimal digits:
x = math.pi
print(x - x%0.01) --> 3.14
As another example of the use of the modulo operator, suppose you want to check whether a vehicle turning a given angle will start to backtrack. If the angle is given in degrees, you can use the following formula:
local tolerance = 10
function isturnback (angle) angle = angle % 360
return (math.abs(angle - 180) < tolerance) end
This definition works even for negative angles:
print(isturnback(-180)) --> true
If we want to work with radians instead of degrees, we simply change the constants in our function:
local tolerance = 0.17 function isturnback (angle)
angle = angle % (2*math.pi)
return (math.abs(angle - math.pi) < tolerance) end
The operation angle%(2*math.pi) is all we need to normalize any angle to a value in the interval [0, 2π).
3.2 Relational Operators
Lua provides the following relational operators:
< > <= >= == ~=
All these operators always produce a boolean value.
The== operator tests for equality; the ~= operator is the negation of equality.
We can apply both operators to any two values. If the values have different types, Lua considers them not equal. Otherwise, Lua compares them according to their types. Specifically, nil is equal only to itself.
Lua compares tables and userdata by reference, that is, two such values are considered equal only if they are the very same object. For instance, after the code
a = {}; a.x = 1; a.y = 0 b = {}; b.x = 1; b.y = 0 c = a
you havea==c but a~=b.
We can apply the order operators only to two numbers or to two strings. Lua compares strings in alphabetical order, which follows the locale set for Lua. For instance, with a Portuguese Latin-1 locale, we have"acai"<"açaí"<"acorde".
Values other than numbers and strings can be compared only for equality (and inequality).
When comparing values with different types, you must be careful: remember that "0" is different from 0. Moreover, 2<15 is obviously true, but "2"<"15"
is false (alphabetical order). To avoid inconsistent results, Lua raises an error when you mix strings and numbers in an order comparison, such as2<"15".
3.3 Logical Operators 23
3.3 Logical Operators
The logical operators are and, or, and not. Like control structures, all logical operators consider both the boolean false and nil as false, and anything else as true. The and operator returns its first argument if it is false; otherwise, it returns its second argument. The or operator returns its first argument if it is not false; otherwise, it returns its second argument:
print(4 and 5) --> 5 print(nil and 13) --> nil print(false and 13) --> false print(4 or 5) --> 4 print(false or 5) --> 5
Both and and or use short-cut evaluation, that is, they evaluate their second operand only when necessary. Short-cut evaluation ensures that expressions like (type(v)=="table"and v.tag=="h1") do not cause run-time errors: Lua will not try to evaluatev.tag when v is not a table.
A useful Lua idiom isx=x or v, which is equivalent to if not x then x = v end
That is, it setsx to a default value v when x is not set (provided that x is not set to false).
Another useful idiom is(a and b)or c, or simply a and b or c, because and has a higher precedence than or. It is equivalent to the C expressiona?b:c, provided thatb is not false. For instance, we can select the maximum of two numbersx and y with a statement like
max = (x > y) and x or y
Whenx>y, the first expression of the and is true, so the and results in its second expression (x), which is always true (because it is a number), and then the or expression results in the value of its first expression,x. When x>y is false, the and expression is false and so the or results in its second expression,y.
The not operator always returns a boolean value:
print(not nil) --> true print(not false) --> true print(not 0) --> false print(not not 1) --> true print(not not nil) --> false
3.4 Concatenation
Lua denotes the string concatenation operator by.. (two dots). If any operand is a number, Lua converts this number to a string. (Some languages use the ‘+’
operator for concatenation, but3+5 is different from 3..5.)
print("Hello " .. "World") --> Hello World
print(0 .. 1) --> 01
print(000 .. 01) --> 01
Remember that strings in Lua are immutable values. The concatenation opera-tor always creates a new string, without any modification to its operands:
a = "Hello"
print(a .. " World") --> Hello World
print(a) --> Hello
3.5 The Length Operator
The length operator works on strings and tables. On strings, it gives the number of bytes in the string. On tables, it gives the length of the sequence represented by the table.
The length operator provides several common Lua idioms for manipulating sequences:
print(a[#a]) -- prints the last value of sequence 'a' a[#a] = nil -- removes this last value
a[#a + 1] = v -- appends 'v' to the end of the list
As we saw in the last chapter, the length operator is unpredictable for lists with holes (nils). It only works for sequences, which we defined as lists without holes. More precisely, a sequence is a table where the numeric keys comprise a set 1, . . . , n for some n. (Remember that any key with value nil is actually not in the table.) In particular, a table with no numeric keys is a sequence with length zero.
Over the years, there have been many proposals to extend the meaning of the length operator to lists with holes, but this extension is easier said than done.
The problem is that, because a list is actually a table, the concept of “length”
is somewhat fuzzy. For instance, consider the list resulting from the following code:
a = {}
a[1] = 1
a[2] = nil -- does nothing, as a[2] is already nil a[3] = 1
a[4] = 1
It is easy to say that the length of this list is four, and that is has a hole at index 2. However, what can we say about the next similar example?
a = {}
a[1] = 1 a[10000] = 1
Should we considera as a list with 10000 elements, where 9998 of them are nil?
Now, the program does this:
3.6 Precedence 25
a[10000] = nil
What is the list length now? Should it be 9 999, because the program deleted the last element? Or maybe still 10 000, as the program only changed the last element to nil? Or should the length collapse to 1?
Another common proposal is to make the# operator return the total number of elements in the table. This semantics is clear and well defined, but not useful at all. Consider all previous examples and think how useful would be such operator for real algorithms over lists or arrays.
Yet more troubling are nils at the end of the list. What should be the length of the following list?
a = {10, 20, 30, nil, nil}
Remember that, for Lua, a field with nil is indistinct from an absent field.
Therefore, the previous table is equal to{10,20,30}; its length is 3, not 5.
You may consider that a nil at the end of a list is a very special case. However, many lists are built by adding elements one by one. Any list with holes that was built that way must have had nils at its end along the way.
Most lists we use in our programs are sequences (e.g., a file line cannot be nil) and, therefore, most of the time the use of the length operator is safe. If you really need to handle lists with holes, you should store the length explicitly somewhere.
3.6 Precedence
Operator precedence in Lua follows the table below, from the higher to the lower priority:
^not # - (unary)
* / %
+
-..< > <= >= ~= ==
andor
All binary operators are left associative, except for ‘^’ (exponentiation) and ‘..’
(concatenation), which are right associative. Therefore, the following expres-sions on the left are equivalent to those on the right:
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)
When in doubt, always use explicit parentheses. It is easier than looking it up in the manual and you will probably have the same doubt when you read the code again.
3.7 Table Constructors
Constructors are expressions that create and initialize tables. They are a dis-tinctive feature of Lua and one of its most useful and versatile mechanisms.
The simplest constructor is the empty constructor, {}, which creates an empty table; we have seen it before. Constructors also initialize lists. For instance, the statement
days = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
will initializedays[1] with the string “Sunday” (the first element of the construc-tor has index 1, not 0),days[2] with “Monday”, and so on:
print(days[4]) --> Wednesday
Lua also offers a special syntax to initialize a table record-like, as in the next example:
a = {x=10, y=20}
This previous line is equivalent to these commands:
a = {}; a.x=10; a.y=20
The original expression, however, is faster, because Lua creates the table already with the right size.
No matter what constructor we use to create a table, we can always add fields to and remove fields from the result:
w = {x=0, y=0, label="console"}
x = {math.sin(0), math.sin(1), math.sin(2)}
w[1] = "another field" -- add key 1 to table 'w' x.f = w -- add key "f" to table 'x' print(w["x"]) --> 0
print(w[1]) --> another field print(x.f[1]) --> another field w.x = nil -- remove field "x"
However, as I just mentioned, creating a table with a proper constructor is more efficient, besides being more elegant.
We can mix record-style and list-style initializations in the same constructor:
3.7 Table Constructors 27
polyline = {color="blue", thickness=2, npoints=4,
{x=0, y=0}, -- polyline[1]
{x=-10, y=0}, -- polyline[2]
{x=-10, y=1}, -- polyline[3]
{x=0, y=1} -- polyline[4]
}
The above example also illustrates how we can nest constructors to represent more complex data structures. Each of the elements polyline[i] is a table representing a record:
print(polyline[2].x) --> -10 print(polyline[4].y) --> 1
Those two constructor forms have their limitations. For instance, you cannot initialize fields with negative indices, nor with string indices that are not proper identifiers. For such needs, there is another, more general, format. In this format, we explicitly write the index to be initialized as an expression, between square brackets:
opnames = {["+"] = "add", ["-"] = "sub", ["*"] = "mul", ["/"] = "div"}
i = 20; s = "-"
a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}
print(opnames[s]) --> sub print(a[22]) -->
---This syntax is more cumbersome, but more flexible too: both the list-style and the record-style forms are special cases of this more general syntax. The con-structor{x=0,y=0} is equivalent to {["x"]=0,["y"]=0}, and the constructor {"r","g","b"} is equivalent to {[1]="r",[2]="g",[3]="b"}.
You can always put a comma after the last entry. These trailing commas are optional, but are always valid:
a = {[1]="red", [2]="green", [3]="blue",}
This flexibility frees programs that generate Lua constructors from the need to handle the last element as a special case.
Finally, you can always use a semicolon instead of a comma in a constructor.
I usually reserve semicolons to delimit different sections in a constructor, for instance to separate its list part from its record part:
{x=10, y=45; "one", "two", "three"}