25.1 Introduction
Every year around Christmas there is a surge of activity on the Codea Forums and on this tutorial site. A common theme in the comments and emails that we receive, is that this site assumes quite a bit of knowledge and as Codea appears to be attractive to first time programmers there are some gaps which need to be filled.
This tutorial is an attempt to fill in some of those gaps. In particular, we will try and provide a quick primer on the Lua language which Codea uses. To ensure that Codea does not incur the wrath of Apple, there are some features of the Lua language which have been disabled in Codea. We will discuss these briefly but the list is diminishing with every new release of Codea and there isn't anything which will likely cause you any problems.
25.2 Lua
Lua is a light weight scripting language written in C. By light weight we don't mean that it isn't capable of writing complicated code but that the syntax is stream lined and straight forward. This makes it a great first language to learn.
Lua means "moon" in Portugese, it is the evolution of another language called SOL (Portugese for "sun"). It was created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group (Tecgraf) at the Pontifical Catholic University of Rio de Janeiro, in Brazil (hence the Portugese naming).
25.3 Variables and Data Types
Variables are memory locations that hold data. There are three kinds of variables in Lua: global variables, local variables, and table fields. All variables are global unless defined otherwise. Defining an identifier as local is done using the local keyword. Local identifiers are not visible outside the block in which they were declared, but are visible inside sub-blocks. This is called lexical scoping. For Codea a tab is considered a chunk, which translates as follows:
- If you define a variable within any of the tabs it will, by default be treated as global.
- If you define a local variable within a tab (but outside a function), you will only be able to access it within that tab.
- If you define a local variable within a function, you can only access it from within that function. However, if a function is enclosed in another function, then it has access to all the local variables of that function. In these circumstances, the external local variable is called an "upvalue". We cover this in Interlude 7.
Global identifiers are stored in the implicit global environment table, which can explicitly be accessed through the name _G (see section 25.5).
Variable names must start with a letter or underscore and can contain letters, digits or underscores. Defined keywords in Lua can not be used as a variable name (e.g. and, break, do, else and end). As a convention, variables in capitals starting with an underscore (e.g. _G or _VERSION) are reserved for internal Lua global variables.
Lua supports only a small number of data types. The ones you will use the most are boolean (true or false), numbers, strings and tables.
By default, Lua's number type is represented by double-precision floating-point numbers. However, Codea's Lua interpreter uses another internal representation for numbers: single-precision float. This gives a precision of 6 to 9 significant decimal digits and a range for positive values of between about 1.4e−45 to about 3.4e+38 (reference from Codea Wiki).
Lua strings can hold any 8-bit character, including embedded zeros. Strings can be enclosed in single or double quotes, pick one style and stick with it, we tend to go with double quotes. Strings in Lua are immutable values, and thus you cannot change a character inside a string. You can also delimit literal strings using matching double square brackets. For example:
aLongString = [[
line 1
line 2
line 3
line 4
]]
Boolean variables are a relatively recent introduction to Lua. The boolean type has two values, true and false. Conditional tests consider false and nil as false and anything else as true (including 0 and "").
Lua is a dynamically typed language, which means that variables do not have a defined type, they get their type based on the data assigned to them. This means that the following is a valid chunk of code in Lua (albeit probably not best practise as it would make your program hard to follow):
x = 1
print("x as an integer: " .. x)
x = 3.141592654
print("x as a float or real number: " .. x)
x = 0xFE
print("x as a hexadecimal converted to decimal: " .. x)
x = "now a string"
print("x as a string: " .. x)
x = true
print(x)
x = {value = 999}
print("x as a table: " .. x.value)
x = function(n) return n*2 end
print("x as a function: " .. x(2))
A few comments about the preceding code and some general observations on variables:
- Lua is case sensitive, so x and X are different variables.
- In the example above, when we store a hexadecimal value in x it is automatically converted and printed as a decimal.
- You can concatenate (i.e. join) two strings or a string and a number using the".." operator. Numbers are automatically converted to strings in this situation. You can't concatenate a boolean.
- This automatic conversion (or to use the technical term - coercion, works the other way around as well, a string will be treated as a number if used in that context. Any arithmetic operation applied to a string tries to convert this string to a number. Note that comparison operators (== ~= < > <= >=) do not coerce their arguments. Thus a number is not equal to its string representation.
- Each line of code is terminated by a new line. You can optionally use a semi-colon (as used in C, but it isn't recommended).
- You can do multiple assignments on the one line (e.g. x, y = 1, 2), and you can use this to swap two variables (e.g. x, y = y, x).
- A variable that hasn't been assigned a value will be nil by definition.
- Unlike C, the value 0 is not a false test condition in Lua, only nil or false is. You can use the fact that nil equates to false to assign default values to a variable (e.g. x = x or 2 will assign the variable x a value of 2 if it hasn't been previously assigned a value).
- Note that you can assign a function to a variable. As an example, we make use of this in Codea to pass call back functions to our Button class to indicate what function to call when a button is tapped. The technical term for this feature is first-class functions. As such, they can be created during runtime, stored in variables, and passed to and returned from other functions.
25.4 Tables
Tables in Lua are amazingly versatile. Which is just as well since they are the only built in composite data type available. The technical term for tables in Lua is a hashed heterogeneous associative array and they are worthy of a separate tutorial, which is exactly what we have done. You can access our tutorials on tables here:
- Understanding Tables;
- Converting a string to table and table to string;
- Saving and Loading complicated tables; and
- Classes in Lua and Codea.
25.5 The Global Variable Table _G
This section goes beyond the scope of this tutorial, so feel free to skip ahead. It is included here out of interest. We mentioned above that Lua has a number of internal variables. One of these is _G, a global variable which points to the global environment. It includes all of the global variables and functions and even includes a reference to itself! It is sometimes useful to understand what has been defined globally and you can use the following to display the contents of _G:
for k, v in pairs(_G) do print(k, v) end
25.6 Userdata
Userdata are variables that encapsulate arbitrary C/C++ data within a Lua interface. Many Lua modules extend the capabilities of Lua by binding external libraries, including the creation of new types as userdata. Userdata variables can only be created using the C API (i.e. in our context this means the Codea runtime), this can't be done in Lua.
Userdata is largely outside the scope of this tutorial but you need to know that Codea extends Lua with 12 user-defined types such as codeaimage, mesh, matrix, vec2, vec3, touch and color. You can read all about these on the Codea User Defined Types page.
Userdata is also useful when we want to expose C/Objective C functions in Lua via the runtime. Have a look at our tutorials on integrating Game Center, building a Universal App or implementing iAds if you are interested.
25.7 Operators
Lua supports the following arithmetic operators: + (addition), - (subtraction), * (multiplication), / (division), % (modulo), ^ (exponentiation) and - (negation).
The relational operators in Lua are: == (equal), ~= (not equal), < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to). These operators will return a boolean (true or false).
The logical operators in Lua are and, or, and not. As for control structures, all logical operators consider both false and nil as false and anything else as true.
As mentioned in section 25.3, the string concatenation operator in Lua is denoted by two dots ('..'). If either operand is a number, then it is converted to a string before joining.
The length operator is denoted by #. The length of a string is its number of bytes (i.e. characters). The length of a table is more complicated. For a table used as a simple array the length operator will work as expected and return the number of elements in the array. For more complicated tables, the returned length can be any of the indices that directly precedes a nil value. You can read more about this in our tutorial on tables.
25.8 Classes
In Interlude 11 we spoke about the use of classes in Codea. Codea comes with a built-in global function called class() that is used to emulate the functionality of a class data structure using a table (and metatable). We use classes extensively in our tutorials but we haven't explained the difference between the "." and ":" operators when applied to a class. As this has been the subject of a number of questions, we will cover the proper usage in this section.
In order to demonstrate, we will first construct a simple ship class. By convention the base class name starts with a capital, while instances of the class have names which start in a lower case letter.
The Codea class() constructor is a function which, when called, sets up a new table and attaches the class metatable to it. The metatable redirects unrecognized events to the class method table (as well as possibly handling events itself). This is covered in some depth on the Codea wiki.
Ship = class()
function Ship:init( hitPoints )
self.points = hitPoints or 100
end
function Ship:hit( damage )
self.points = self.points - damage
print( self.points )
end
In our Main tab, setup() function we may then instantiate our class as follows:
One of the potentially confusing parts of the class definition is where does "self" come from and why do we use it? The self variable is created automatically by calling class() and provides a reference to the object created when we instantiate a class. This means that when we change the points variable using self.points we only change it for that object, not for every object of class Ship. So in our example above self = myShip.
Try converting the Ship class so that it uses points instead of self.points. If you do this and have created another ship (e.g. myOtherShip = Ship()), then every time you call myShip:hit(20) this will also reduce the hit points of myOtherShip, which is usually not what you want.
If our ship gets hit then we want to record the damage and print out the current hit points using the hit(damage) function of our class. There are two ways you could do this (the right way and the wrong way!). In the Main tab of your program, you could use:
myShip:hit(20)
or you could try to use:
myShip.hit(20)
Which is correct? To work this out we have to understand the difference between the two statements. Using ":" we are actually passing two parameters to hit(), the damage and a hidden reference to the object being hit (i.e. self). So myShip:hit(20) is equivalent to myShip.hit(self, 20).
The second option - myShip.hit(20) will throw an error in Codea. If you want to access the class instance variables directly from the Main tab, you can do something like:
myShip.points = myShip.points - 20
This will operate as expected, but only if you have defined it as self.points in your class.
25.9 Other Resources
If what is provided here isn't sufficient then have a look at the official Lua Tutorial site, which has a MUCH more detailed treatment on Lua. The Codea reference documentation is also very useful, as is the active forum and wiki (in particular have a look at the Hints and Tips page which contains references to items not covered elsewhere).
Codea (v1.4) uses version 5.1 of Lua (you can determine the current version of Lua using the statement print(_VERSION)). The definitive treatment of the Lua language can be found at the official on-line Lua 5.1 Reference Manual.
No comments:
Post a Comment