Contacts

Introduction to Lua. Want to know everything. Lua Language Learning lua from scratch

A close friend of mine recently went for a job interview with a local game development company. I'm not going to name names here, just say it was kind of a big Game Development boutique in Vancouver. He didn’t get a job, but today we’re not talking about him. Personally, I believe that one of the reasons was due to his lack of friendly relations with the scripting language that they use.

Introduction

I am in this area as I teach game programming to students, but this is the topic that I have not paid enough attention to in the past. We cover Unreal Script as part of the Using Existing course. But we did not actually consider the script engine as part of the utilities or part of the engine. So, armed with a website, I decided to break this little barrier. The result is described in this document.

The only thing, I'm not sure how big this document will be. I can break it down into several small parts or publish it in its entirety in a long tirade from start to finish. Anyway, I will decide this a little later, when I put my notes in a more meaningful and consistent format.

Why and why not?

First of all, why use a scripting language? Most of the game logic can be written in a scripting language for various purposes, rather than being programmed as part of the game engine. For example, loading or initializing a level. After loading the level, you may want to translate the scene to the game plan, or you may want to show some preliminary text. Using a scripting system, you could make certain objects in the game perform specific tasks. Also, consider implementing artificial intelligence. Non-Play Characters need to know what to do. Programming each NPC “manually” in the body of the game engine will unnecessarily complicate the task. When you want to change the behavior of an NPC, you will have to recompile your project. With a scripting system, you can do this interactively by changing the behavior and saving the settings.

I touched on this problem a little in the last paragraph, we will talk about this a little later. The question is, why not write logic exclusively in C / C ++? Simply put, that in the long term the programmer has the fact that everything falls directly on him and he starts, respectively, from the game code, at the same time he will have to write an engine and utilities, etc. But we can now transfer some of the functionality tasks to the level designers with a simple scripting language. They can start tinkering with the level and optimizing the gameplay. Here's an example:

Let's imagine that Joe, our hapless programmer, writes the entire game engine, tools, and game logic himself. Yes, Joe is going to be in trouble, but let's assume he doesn't care. We also have Brandon, the game designer. Brandon is a pretty smart kid with great ideas for the game. And so our coder Joe crawls out and implements all the game logic using the toolkit he developed based on Brandon's initial project. Everything is fine in the office. The first phase is over, and Joe and Brandon are sitting in the boardroom and reviewing their considerable work. Brandon notices several problems with the gameplay, which is not behaving properly. So Joe goes back to the code and makes the required changes. This process can take a day, at least if it's not a trivial change. Then another day to recompile the project. In order not to lose an extra day, most offices leave the assembly process overnight. So, as we can see, it takes 24 hours before Brandon sees the change he demanded.

Now, let's pretend that our protagonist Joe decides that the implementation of the game logic uses the script engine to his advantage. It will take some time in the beginning, but he feels that it will benefit in the long run. And so, it shifts some functionality from the game engine to the script system of the game. He also writes all the game logic in the previously mentioned scripting system. And so, when he meets Brandon and the designer notices something that does not correspond to his idea, Joe quickly opens the console, makes some changes in the script, restarts the game and already sees the new behavior. Changes can be made immediately and shown immediately, instead of waiting for recompiling. And if Joe was particularly expressive, the scripting system could be used for utilities and available to level designers when building levels. If you follow this path, then with a little training, level designers could set game events themselves, such as triggers, doors, other game events and enjoy life without straining the programmer.

This is a rather contrived example and might be a bit exaggerated, but I hope it shows the difference between the approaches. So what we're trying to do with this model, which means moving towards more automatically managed data. So, essentially, where are we going:

  1. The coder is interested in writing the engine / tools code, not the game logic.
  2. Time was spent writing the game engine / tools.
  3. Designers like to play around with things. Scripting gives them the freedom to design levels and functionality. It also gives them more flexibility to experiment with things that they would normally involve a programmer for.
  4. You don't have to recompile if you want to change the functionality of the game. Just change the script.
  5. You want to break the connection between machine and game code. They should be in two separate parts. This way it will be convenient to use the engine for future sequels (I hope).

I'll make some predictions here. Over the next 5 years, level designers will have to do more than just build levels. They must be able to use script for game scenes. Several cutting-edge companies have already taken this approach. Also, you can see this integration method in editors like UnrealEd and Aurora toolset Bioware.

Clarification and ranting

I hope now you have already bought into my words and wanted to include a script component in your game. So the next question is: how the hell do you do this?

What I am going to use for my script component is an injectable script engine Lua... In the beginning, I will say that I am not an expert in Lua, but it is a relatively simple language and does not require tedious learning to master it. Some of the examples that I'll go over later are pretty straightforward. At the end of this document, I'm going to include some additional reference material. To be fair, there are other scripting languages ​​like Small, Simkin, Python, Perl. However, Lua is a nice and clean language. This is a really good advantage.

Lua is open source. This is good because: (a) You get the source for the language and you can dig as much as you like, (b) it's free. You can use it in commercial applications without throwing money around. Well, for non-commercial projects you yourself understand for free == good.

So who is currently using Lua? Lua is written by a sharashka bureau and is used only by the poor? Mmm ... not really. Lua did not appear yesterday and was used by well-known personalities:

  • Lucasarts
    • Grim fandango
    • Escape from Monkey Island
  • Bioware
    • Neverwinter nights

Ok, enough with the who-is-who of the lua developers. You can see this for yourself on the lua website.

Let's start really simple. The first thing we need to build will show us how the lua interpreter is used. What is required for this:

  1. Getting the Lua interpreter code.
  2. Setting up your development environment.
  3. Building the interpreter from scratch.

Hey, I thought you said enough rant?

Is that enough? So let's get down to business. You can get all the Lua source code from the official website. I would also like to take a second and point out that there is a new version of lua 5.0 on the horizon. I am not going to discuss this version in this article. I'll deal with it later, but for now, we'll use 4.0.1.

The first thing we'll do is build the lua library. Thus, we do not need to include the sources every time we build the project. It is not difficult and this is not the purpose of our lessons. Therefore, I have included the library in advance as part of this article. I used a static library for this example. Yes, maybe I would have built it as a DLL, but for a scripting system, a static library is a little faster. Notice, not much, but faster.

Our guest today is a real fighter of the hidden front. You may have seen it in games (World of Warcraft, Angry Birds, X-Plane, S.T.A.L.K.E.R.) or Adobe products (Lightroom), but you never even thought about its existence. Meanwhile, this language is already almost 25 years old, and all this time, it imperceptibly made our virtual life a little better.

Quick reference

Lua would have been coined in 1993 by the Catholic University of Rio de Janeiro. The name is translated from Portuguese as Luna, and the creators are convincingly asking not to write LUA, so that, God forbid, someone does not take the name for an abbreviation. It is a multi-paradigm scripting language using the OOP prototype model.

Typing is dynamic here, and metatables are used to implement inheritance, that is, it is a great tool for extending the capabilities of your product. Moreover, due to its compactness, it is suitable for use on almost any platform. Judge for yourself: tarball Lua 5.3.4 weighs only 296 kilobytes (uncompressed - 1.1 megabytes), the interpreter (written in C) for Linux - from 182 to 246 kilobytes, and the standard set of libraries - another 421 kilobytes.

The code

In appearance and capabilities, Lua looks like another attempt to remake JavaScript, if not for the fact that the latter appeared two years later. See for yourself:

Let's start with the traditional one:

print ("Hello World")

Agree, familiar and not very informative. A more interesting example from the point of view of familiarity with Lua is calculating the factorial of an entered number:

Function fact (n)
if n == 0 then
return 1
else
return n * fact (n-1)
end
end

Print ("enter a number:")
a = io.read ("* number") - read a number
print (fact (a))

Everything is very clear. By the way, Lua supports parallel assignment:

And finally, a fairly simple example using libraries:

#include
#include
#include
#include
#include

Int main (void) (
char buff;
int error;
lua_State * L = lua_open (); / * opens Lua * /
luaopen_base (L); / * opens the basic library * /
luaopen_table (L); / * opens the table library * /
luaopen_io (L); / * opens the I / O library * /
luaopen_string (L); / * opens the string lib. * /
luaopen_math (L); / * opens the math lib. * /

While (fgets (buff, sizeof (buff), stdin)! = NULL) (
error = luaL_loadbuffer (L, buff, strlen (buff), "line") ||
lua_pcall (L, 0, 0, 0);
if (error) (
fprintf (stderr, "% s", lua_tostring (L, -1));
lua_pop (L, 1); / * pop error message from the stack * /
}
}

Lua_close (L);
return 0;
}

Advantages and disadvantages

So what is good about Lua?

First, as already noted, by its compactness, and coupled with the fact that the source code is written in C, you get full interaction with one of the most popular languages ​​on the planet and a wide range of available platforms.

Development environments

LDT (Lua Development Tools) for Eclipse - an extension for one of the most popular IDEs;

ZeroBrane Studio is a specialized environment written in Lua;

Decoda is not the most popular cross-platform IDE, but it will work as an alternative;

SciTE is a good editor with full Lua support;

WoWUIDesigner - guess which game this environment helps to process scripts, including Lua?

useful links

http://www.lua.org/home.html - the official site with all the necessary information, tutorial, books, documentation, and even some specific humor;

http://tylerneylon.com/a/learn-lua/ is a great tutorial from Tyler Neylon. Suitable for experienced programmers who know English well (however, there will be no big problems with the dictionary either) and just wants to broaden their horizons;

https://zserge.wordpress.com/2012/02/23/lua- for-60-minutes/ - Lua basics in 60 minutes from a programmer who is clearly not indifferent to this language. In Russian;

http://lua-users.org/wiki/LuaTutorial - wiki tutorial

https://youtube.com/watch?v=yI41OL0-DWM- video tutorials on YouTube that will help you visually understand the IDE setup and the basic principles of the language.

Lua scripts

A script written in Lua does not have any special function from which to start its execution. A script can be thought of simply as a set of commands (instructions) that are executed starting from the first instruction.

A script can be either very simple, consisting of just one command, or very complex, containing tens, hundreds or even thousands of instructions. Consecutive instructions can be separated by semicolons (;). However, this requirement is optional, so all of the code below is syntactically correct:

Working with variables in Lua

Variables are used to store values ​​during script execution.

Variable names in Lua

Variable names (identifiers) in Lua can be any sequence of letters, numbers and underscores that do not start with a number.

note

Lua is case-sensitive, so abc, Abc, ABC are different names.

The table below shows words that are reserved by the Lua language and cannot be used in variable names:

and break do else elseif

end false for function if

in local nil not or

repeat return then true until

In addition, all names beginning with an underscore followed by capital letters (for example, _VERSION) are also reserved.

What variables are there in Lua?

Variables in Lua can be global or local. If a variable is not explicitly declared as local, it is considered global.

Lua global variables

The global variable appears when the first value is assigned to it. Before the first value is assigned, the call to the global variable yields nil.

MsgBox (tostring (g)) -> nil

MsgBox (tostring (g)) -> 1

A global variable exists as long as the script execution environment exists and is available to any Lua code running in that environment.

If necessary, you can explicitly delete a global variable by simply assigning a nil value to it.

g = 1 - create a global variable g with the value 1

g = nil - delete the global variable g

MsgBox (tostring (g)) -> nil

All global variables are fields of a regular table called the global environment. This table is accessible through the global variable _G. Since the fields of the global environment are all global variables (including _G itself), then _G._G == _G.

Lua local variables

Any local variables must be declared explicitly using the local keyword. You can declare a local variable anywhere in the script. The declaration can include assigning an initial value to the variable. If no value is assigned, the variable contains nil.

local a - declare a local variable a

local b = 1 - declare a local variable b, assign it the value 1

local c, d = 2, 3 - declare local variables c and d, assign them values ​​2 and 3

The scope of a local variable begins after the declaration and continues to the end of the block.

Note

The scope of a variable is a piece of program code within which you can access the value stored in a given variable.

A block means:

the body of the control structure (if-then, else, for, while, repeat);

function body;

a piece of code enclosed in the keywords do ... end.

If a local variable is defined outside of any block, its scope extends to the end of the script.

local i = 1 - variable i is local within the script

while i<= a do - цикл от 1 до 5

local a = i ^ 2 - variable a is local inside the while loop

MsgBox (a) -> 1, 4, 9, 16, 25

MsgBox (a) ->

if i> 5 then

local a - variable a is local inside then

MsgBox (a) -> 10

MsgBox (a) -> 5 (here reference to global a)

local a = 20 - variable a is local inside the do-end

MsgBox (a) -> 20

MsgBox (a) -> 5 (here reference to global a)

note

Whenever possible, it is recommended to use local variables instead of global ones. This will avoid "cluttering" the global namespace and provide better performance (since accessing local variables in Lua is somewhat faster than globals).

Lua data types

What data types does Lua support?

Lua supports the following data types:

1. Nil (nothing). Corresponds to the fact that a variable has no value. This type is represented by a single value, nil.

2. Boolean (logical). This type includes the values ​​false and true.

When performing logical operations, nil is considered false. All other values, including the number 0 and the empty string, are considered true.

3. Number (numeric). Serves to represent numeric values.

Numeric constants can contain an optional fractional part and an optional decimal order, specified by the characters "e" or "E". Integer numeric constants can be specified in hexadecimal using the 0x prefix.

Examples of valid numeric constants: 3, 3.0, 3.1415926, 314.16e-2, 0xff.

4. String (string). Serves to represent strings.

String values ​​are specified as a sequence of characters, enclosed in single or double quotes:

a = "this is a string"

b = "this is the second line"

Strings enclosed in double quotes can interpret C-like escape sequences (escape sequences) that start with the character "\" (backslash):

\ b (space),

\ n (line feed),

\ r (carriage return);

\ t (horizontal tab),

\\ (backslash);

\ "" (double quote);

\ "(single quote).

note

A character in a string can also be represented by its own code using an escape sequence:

where ddd is a sequence of no more than three digits.

In addition to quotation marks, double square brackets can also be used to define a string:

Defining a string with double square brackets allows all escape sequences to be ignored, i.e. the string is created exactly as described:

local a = [] in Lua] =]

There will be a term: "definition of string [] in Lua"

5. Function. Functions in Lua can be written to variables, passed as parameters to other functions, and returned as the result of executing functions.

6. Table. A table is a set of key-value pairs, which are called fields or table elements. Both the keys and the values ​​of the fields in the table can be of any type except nil. Tables do not have a fixed size: you can add an arbitrary number of elements to them at any time.

More details - in the article "Creating tables in Lua"

7. Userdata (user data). It is a special data type. Values ​​of this type cannot be created or modified directly in a Lua script.

Userdata is used to represent new types created in the script calling program or in libraries written in C. For example, the Lua extension libraries for "CronosPRO" use this type to represent objects such as:

data banks (class Bank);

databases (Base class);

records (class Record), etc.

8. Thread (thread). Corresponds to the flow of execution. These streams are not connected in any way with the operating system and are supported exclusively by means of Lua itself.

How to set the type of a variable in Lua?

Lua does not explicitly set the type of a variable. The type of a variable is set at the time a value is assigned to the variable. Any variable can be assigned a value of any type (regardless of what type of value it previously contained).

a = 123 - variable a is of type number

a = "123" - now the variable a is of type string

a = true - now the variable a is of type boolean

a = () - now the variable a is of type table

note

Variables of type table, function, thread and userdata do not contain the data itself, but store references to the corresponding objects. When assigning, passing to a function as an argument and returning from a function as a result, objects are not copied, only references to them are copied.

a = () - create a table. A reference to the table is placed in the variable a

b = a - variable b refers to the same table as a

a = 10 - the element of the table with index 1 is assigned the value 10

MsgBox (b) -> "10"

MsgBox (a) -> "20"

The rest of the data are immediate values.

MsgBox (a) -> "20"

MsgBox (b) -> "10"

How to get the type of a variable in Lua?

The type of the value stored in a variable can be found out using the standard function type. This function returns a string containing the name of the type ("nil", "number", "string", "boolean", "table", "function", "thread", "userdata").

t = type ("this is a string") - t is equal to "string"

t = type (123) - t equals "number"

t = type (type) - t is "function"

t = type (true) - t is "boolean"

t = type (nil) - t is "nil"

t = type (CroApp.GetBank ()) - t equals "userdata"

How to convert the type of a variable in Lua?

Lua automatically converts numbers to strings and vice versa as needed. For example, if a string value is an operand in an arithmetic operation, it is converted to a number. Likewise, a numeric value that occurs where a string is expected will be converted to a string.

a = "10" + 2 - a equals 12

a = "10" + 2 - a is equal to "10 + 2"

a = "-5.3e-10" * "2" - a equals -1.06e-09

a = "string" + 2 - Error! Cannot convert "string" to number

Any value can be explicitly converted to a string using the standard tostring function.

a = tostring (10) - a equals "10"

a = tostring (true) - a equals "true"

a = tostring (nil) - a equals "nil"

a = tostring ((= "this is field 1")) - a is equal to "table: 06DB1058"

From the previous example, you can see that the contents of tables are not converted by the tostring function. This transformation can be done using the render function.

a = render (10) - a equals "10"

a = render (true) - a equals "true"

a = render (nil) - a is "nil"

a = render ((= "this is field 1")) - a is equal to "(=" this is field 1 ")"

You can use the standard tonumber function to explicitly convert a value to a number. If the value is a string that can be converted to a number (or is already a number), the function returns the conversion result, otherwise it returns nil.

a = tonumber ("10") - a equals "10"

a = tonumber ("10" .. ". 5") - a equals 10.5

a = tonumber (true) - a is "nil"

a = tonumber (nil) - a is "nil"

Arranging comments in Lua

A comment in Lua begins with two minus signs (-) and continues to the end of the line.

local a = 1 - single line comment

If two open square brackets ([[)] immediately follow the “-” characters, the comment is multiline and continues up to two closing square brackets (]]).

local a = 1 - [[multiline

a comment ]]

Double brackets in comments can be nested. In order not to confuse them, an equal sign (=) is inserted between the parentheses:

local a = [[Kronos Company]] - [= [

local a = [[Kronos Company]]

The number of "=" symbols determines the nesting:

local a = [= [definition of some string [] in Lua language] =] - [== [

local a = [= [definition of some string [] in Lua language] =]

Lua Operations

The following types of operations can be used in expressions written in Lua:

1. Arithmetic operations.

Lua supports the following arithmetic operations:

+ (addition);

- (subtraction);

* (multiplication);

/ (division);

^ (exponentiation);

% (remainder of the division).

note

Arithmetic operations apply to both numbers and strings, which in this case are converted to numbers.

2. Operations of comparison.

The following comparison operations are allowed in Lua:

== (equal);

~ = (not equal);

< (меньше);

> (more);

<= (меньше или равно);

> = (greater or equal).

note

Comparison operations always return the Boolean value true or false.

The rules for converting numbers to strings (and vice versa) do not work for comparisons, that is, the expression "0" == 0 results in false.

3. Logical operations.

Logical operations include:

and (logical AND).

The and operator returns its first operand if it is false or nil. Otherwise, the operation returns the second operand (moreover, this operand can be of any type).

a = (nil and 5) - a is nil

a == (false and 5) - a equals false

a == (4 and 5) - a equals 5

or (logical OR).

The or operator returns the first operand if it is not false or nil, otherwise it returns the second operand.

a == (4 or 5) - a equals 4

a == (false or 5) - a equals 5

note

Boolean operations and and or can return values ​​of any type.

The logical operators and and or evaluate the value of the second operand only if it needs to be returned. If not required, the second operand is not evaluated. For instance:

a == (4 or f ()) - the function f () will not be called

not (logical NOT).

The not operation always returns true or false.

4. Operation of concatenation.

To concatenate (join) strings, use the operation ... (two dots).

a = "Kronos" .. "-" .. "Inform" - the variable a will receive the value "Kronos-Inform"

note

If one or both operands are numbers, they are converted to strings.

a = 0..1 - variable a will receive the value "01"

5. The operation of obtaining the length.

Lua defines a length operator # that can be used to get the length of a string.

a = "string"

len = #a - len is 6

len = # "another line" - len is 10

note

You can also use the # operation to find out the maximum index (or size) of an array. More details - in the article "Working with arrays in Lua".

Operation precedence in Lua

In Lua, operations are performed according to the following priority (in descending order):

2.not # - (unary)

6. < > <= >= ~= ==

Calling scripts from forms

Each form (including nested forms) has a separate script associated with it, which usually contains functions that handle the events of the form and its elements.

When the form is launched, its script is loaded into the global environment. When an event of a form or its element occurs, the system calls the handler function associated with this event.

It should be noted that the form script, although it does not contain a call to the module function, is in fact a module. This means that variables declared in the form script without the local keyword are not moved to the global environment and are available only within this script. If you need to make a value available to scripts of other forms, it must be explicitly defined in the global _G table:

local a = _G.var

Statement blocks

The main Lua operators are:

assignment;

conditional operator;

operators for organizing loops.

A group of statements can be combined into a block (compound statement) using the do… end construction.

do - the beginning of the block

<оператор1>- block body

<оператор2>

<операторN>

end - the end of the block

The block opens a new scope in which you can define local variables.

a = 5 - global variable a

local a = 20 - inside the do-end local variable a is defined

MsgBox (a) -> 20

MsgBox (a) -> 5 (here the call is already to the global a)

Lua assignment operator

An assignment changes the value of a variable or table field. In its simplest form, an assignment might look like this:

a = 1 - variable a is assigned the value 1

a = b + c - variable a is assigned the sum of values ​​of variables b and c

a = f (x) - the variable a is assigned the value returned by the function f (x)

Lua allows the so-called multiple assignment, when several variables to the left of the assignment operator receive the values ​​of several expressions written to the right of the assignment operator:

a, b = 1.5 * c - a equals 1; b equals 5 * c

If there are more variables than values, nil is assigned to the "extra" variables.

a, b, c = 1, 2 - a is 1; b is 2; c is nil

If there are more values ​​than variables, "extra" values ​​are ignored.

a, b = 1, 2, 3 - a is 1; b is 2; value 3 not used

Multiple assignment can be used to exchange values ​​between variables:

a = 10; b = 20 - a is 10, b is 20

a, b = b, a - now a is 20, b is 10

Conditional statement (if) in Lua

The if statement tests whether the specified condition is true. If the condition is true, the part of the code following the then keyword (then section) is executed. Otherwise, the code following the else keyword is executed (the else section).

if a> b then

return a - if a is greater than b, return a

return b - otherwise, return b

The else section is optional.

if a< 0 then

a = 0 - if a is less than 0, set a to 0

Instead of nested if statements, you can use the elseif construct. For example, the following code:

it will be easier to understand if you replace it with the following:

return "Ivan" - if a is 1

elseif a == 2 then

return "Peter" - if a is 2

elseif a == 3 then

return "Sergey" - if a is 3

return "There is no such player" - if a is none of the above

Lua while loop

The while statement is designed to organize loops with a precondition and has the following form:

while do

... - the body of the cycle

Before each iteration of the loop, the condition is checked :

if the condition is false, the loop ends and control is transferred to the first statement following the while statement;

if the condition is true, the body of the loop is executed, after which all actions are repeated.

while i> 0 do - loop from 10 to 1

t [i] = "field" ..i

a = (3, 5, 8, -6, 5)

while i> 0 do - looking for a negative value in the array

if a [i]< 0 then break end - если найдено, прерываем цикл

i = i - 1 - otherwise go to the next element

if i> 0 then

MsgBox ("Index of negative value:" ..i)

MsgBox ("Array does not contain negative values")

Note

Loop with postcondition (repeat) in Lua

The repeat statement is designed to organize loops with a postcondition and has the following form:

... - the body of the cycle

until

The body of the loop is executed as long as the condition will not become true. The condition is checked after the loop body is executed, therefore, in any case, the loop body will be executed at least once.

Sum the values ​​of the array a until the sum exceeds 10

a = (3, 2, 5, 7, 9)

sum = sum + a [i]

until sum> 10

MsgBox ("Stacked" ..i .. "items. Amount is" ..sum)

You can use the break statement to exit the loop before it completes.

Note

More details about the features of using the break operator - in the article "The break and return statements"

Lua for loops

The for statement is designed to organize loops and can be written in two forms:

simple (numeric for);

extended (universal for).

The simple form of the for statement

The simple form of the for statement looks like this:

for var = exp1, exp2, exp3 do

... - the body of the cycle

The body of the loop is executed for each value of the loop variable (counter) var in the range from exp1 to exp2, with the step exp3.

Note

The step may not be set. In this case, it is taken equal to 1.

for i = 1, 10 do - loop from 1 to 10 with step 1

MsgBox ("i is equal to" ..i)

for i = 10, 1, -1 do - loop from 10 to 1 with a step of -1

MsgBox ("i is equal to" ..i)

note

The expressions exp1, exp2, and exp3 are evaluated only once, before the start of the loop. So, in the example below, the function f (x) will be called to calculate the upper limit of the loop only once:

for i = 1, f (x) do - loop from 1 to the value returned by the function f ()

MsgBox ("i is equal to" ..i)

The loop variable is local to the loop statement and is undefined at its end.

for i = 1, 10 do - loop from 1 to the value returned by f ()

MsgBox ("i is equal to" ..i)

MsgBox ("After exiting the loop, i is equal to" ..i) - Wrong! i is nil

note

The value of a loop variable cannot be changed inside a loop: the consequences of such a change are unpredictable.

To exit the loop before it finishes, use the break statement.

a = (3, 5, 8, -6, 5)

for i = 1, # a do - look for a negative value in the array

if a [i]< 0 then - если найдено...

index = i - store the index of the found value ...

break - and break the loop

MsgBox ("Index of negative value:" ..index)

Note

More details about the features of using the break operator - in the article "The break and return statements")

I am a sentimental programmer. Sometimes I fall in love with programming languages, and then I can talk about them for hours. I will share one of these hours with you.

Lua? What's this?

Lua is a simple embeddable language (it can be integrated with your programs written in other languages), lightweight and straightforward, with one data type, with a uniform syntax. The perfect language to learn.

What for?

Lua might come in handy for you:

* if you are a gamer (plugins for World of Warcraft and many other games)
* if you write games (very often in games, the engine is written in C / C ++, and AI - in Lua)
* if you are a system programmer (you can write plugins for nmap, wireshark, nginx and other utilities in Lua)
* if you are an embedded developer (Lua is very fast, compact and requires very few resources)

1. Learn to program. At least a little. It doesn't matter what language.
2. Install Lua. To do this, either download version 5.2 here (http://www.lua.org/download.html), or look for it in the repositories. Version 5.1 will work too, but be aware that it is very old.

Run all examples from the article in the terminal with a command like "lua file.lua".

First impressions

Lua is a dynamically typed language (variables get types "on the fly" depending on the assigned values). You can write in it in both an imperative and an object-oriented or functional style (even if you don't know how it is, it's okay, keep reading). Here is Hello world in Lua:

My first lua app: hello.lua print "hello world"; print ("goodbye world")

What can already be said about the language:

* single line comments start with two hyphens "-"
* brackets and semicolons can be omitted

Language Operators

The set of conditionals and loops is pretty typical:

Conditional statements (there may be no else branches) if a == 0 then print ("a is zero") else print ("a is not zero") end - abbreviated form if / elseif / end (instead of switch / case) if a == 0 then print ("zero") elseif a == 1 then print ("one") elseif a == 2 then print ("two") else print ("other") end - counter loop for i = 1, 10 do print (i) end - loop with precondition b = 5 while b> 0 do b = b - 1 end - loop with postcondition repeat b = b + 1 until b> = 5

THINK: what could the "for i = 1, 10, 2 do ... end" loop mean?

In expressions, you can use the following operators on variables:

* assignment: x = 0
* arithmetic: +, -, *, /,% (remainder of division), ^ (exponentiation)
* boolean: and, or, not
* comparison:>,<, ==, <=, >=, ~ = (not-equal, yes-yes, instead of the usual "! =")
* string concatenation (operator ".."), for example: s1 = "hello"; s2 = "world"; s3 = s1..s2
* length / size (operator #): s = "hello"; a = #s ('a' will be 5).
* getting an element by index, e.g .: s

For a long time there were no bitwise operations in the language, but in version 5.2 the bit32 library appeared, which implements them (as functions, not as operators).

Data types

I lied to you when I said that the language has one data type. He has a lot of them (like every serious language):

* nil (nothing at all)
* boolean numbers (true / false)
* numbers (numbers) - no division by integers / real. Just numbers.
* strings - by the way, they are very similar to strings in pascal
* functions - yes, a variable can be of the "function" type
* thread
* arbitrary data (userdata)
* table

If everything is clear with the first types, then what is userdata? Remember that Lua is an embedded language and usually works closely with components of programs written in other languages. So, these "foreign" components can create data according to their needs and store this data together with lua objects. So, userdata is the underwater part of the iceberg, which from the point of view of the lua language is not needed, but we simply cannot ignore it.

And now the most important thing in the language is tables.

Tables

I lied to you again when I said that the language has 8 data types. You can assume that he is one: everything is tables (by the way, this is also not true). A table is a very elegant data structure, it combines the properties of an array, a hash table ("key" - "value"), structure, object.

So, here is an example of a table as an array: a = (1, 2, 3) - an array of 3 elements print (a) - will output "2", because indices are counted from one - A table is a sparse array (which does not have all elements) a = () - empty table a = 1 a = 5

THINK: What is a for a sparse array?

In the example above, the table behaves like an array, but in reality - we have keys (indexes) and values ​​(array elements). And at the same time, keys can be of any type, not just numbers:

A = () a ["hello"] = true a ["world"] = false a = 1 - or like this: a = (hello = 123, world = 456) print (a ["hello")) print ( a.hello) is the same as a ["hello"], although it looks like a structure with fields

By the way, since the table has keys and values, you can loop over all the keys and their corresponding values ​​in a loop:

T = (a = 3, b = 4) for key, value in pairs (t) do print (key, value) - prints "a 3", then "b 4" end

But what about the objects? We will learn about them a little later, first - about the functions.

Functions

Here's an example of a common function.

Function add (a, b) return a + b end print (add (5, 3)) - will print "8"

Language functions allow you to take multiple arguments and return multiple arguments. For example, arguments that are not explicitly specified are assumed to be nil.

THINK: Why would you want to return multiple arguments?

Function swap (a, b) return b, a end x, y = swap (x, y) - by the way, this can be done without a function: x, y = y, x - and if the function returns several arguments, - and you don't need them - ignore them with - special underscore variable "_" a, _, _, d = some_function ()

Functions can take a variable number of arguments:

In the prototype, the variable number of arguments is written as ellipsis function sum (...) s = 0 for _, n in pairs (arg) do - in the function they are referred to as the table "arg" s = s + n end return a end sum (1, 2, 3) - will return 6 sum (1, 2, 3, 4) - will return 10

Since functions are a full-fledged data type, you can create function variables, or you can pass functions as arguments to other functions.

A = function (x) return x * 2 end - function that multiplies by 2 b = function (x) return x + 1 end - function that increases by 1 function apply (table, f) result = () for k, v in pairs (table) do result [k] = f (v) - replace the element with some function from this element end end - THINK: what calls t = (1, 3, 5) will return apply (t, a) apply (t, b)

Objects = functions + tables

Since we can store functions in variables, then we can also in the fields of tables. And this already turns out, as it were, methods. For those who are not familiar with OOP, I will say that its main benefit (at least in Lua) is that the functions and data with which they work are side by side - within the same object. For those who are familiar with OOP, I will say that there are no classes here, but inheritance is prototypical.

Let's move on to examples. We have an object, say, a light bulb. She knows how to burn and not burn. Well, there are two things you can do with it - turn it on and off:

Lamp = (on = false) function turn_on (l) l.on = true end function turn_off (l) l.on = false end are just functions for working with the turn_on (lamp) turn_off (lamp) structure

And if we turn the light bulb into an object, and turn the turn_off and turn_on functions into fields of the object, we get:

Lamp = (on = false turn_on = function (l) l.on = true end turn_off = function (l) l.on = false end) lamp.turn_on (lamp) lamp.turn_off (lamp)

We are forced to pass the light bulb object itself as the first argument, because otherwise our function will not know which light bulb it needs to work with in order to change the on / off state. But in order not to be verbose, Lua has a shorthand that is usually used - lamp: turn_on (). In total, we already know several such syntax simplifications:

Lamp: turn_on () - the most common notation lamp.turn_on (lamp) - from the point of view of syntax, this is also correct lamp ["turn_on"] (lamp) - and this

Continuing to talk about abbreviations, functions can be described not only explicitly, as fields of a structure, but also in a more convenient form:

Lamp = (on = false) - through a period, then the argument must be specified function lamp.turn_on (l) l.on = true end - through colons, then the argument is implicitly set as the variable "self" - "self" - and there is the lamp for which the function lamp was called: turn_off () self.on = false end

Interesting?

Special functions

Some table function (method) names are reserved and have a special meaning:

* __add (a, b), __sub (a, b), __div (a, b), __mul (a, b), __mod (a, b), __pow (a, b) - called when arithmetic operations are performed on table
* __unm (a) - unary minus operation (when they write something like "x = -x")
* __lt (a, b), __le (a, b), __eq (a, b) - calculate the comparison result (<, <=, ==)
* __len (a) - called when "#a" is done
* __concat (a, b) - called when "a..b"
* __call (a, ...) - called when "a ()". Variable arguments are arguments when called
* __index (a, i) - call to a [i], provided that no such element exists
* __newindex (a, i, v) - creating "a [i] = v"
* __gc (a) - when an object is discarded by garbage collection

By replacing these methods, you can overload operators and use the syntax of the language for your own purposes. The main thing is not to overdo it.

Inheritance

For those who don't know OOP, inheritance allows you to extend the functionality of an existing class. For example, just a light bulb can turn on and off, and a super-light bulb will also change its brightness. Why do we need to rewrite the turn_on / turn_off methods when we can reuse them?

Lua has the concept of a meta-table for this, i.e. ancestor tables. Each table has one parent table, and the child table can do everything that the parent can do.

Let's say we've already created the lamp table object. Then the super light bulb will look like this:

Superlamp = (brightness = 100) - specify the parent table setmetatable (superlamp, lamp) - and its methods are now available superlamp: turn_on () superlamp: turn_off ()

Expansion of functionality

There are many types of parent tables (well, strings and tables, for sure, numbers and booleans, and nil does not). Let's say we want to add all lines using the "+" operator, not "..". To do this, replace the "+" (__add) function for the parent table of all rows:

S = getmetatable ("") - got the parent table of the row s .__ add = function (s1, s2) return s1..s2 end - changed the method - check a = "hello" b = "world" print (a + b) - will write "helloworld"

Actually, we can still replace the print function with "print = myfunction", and many other hacking things can be done.

Scopes

Variables are global and local. When created, all variables in Lua are global.

THINK: why?

To specify the local scope, write the local keyword:

Local x local var1, var2 = 5, 3

Don't forget about this word.

Error processing

Often, if errors occur, you need to stop executing a particular function. You can, of course, do a lot of checks and call "return" if something goes wrong. But this will increase the amount of code. Lua uses something like exceptions.

Errors are thrown using the error (x) function. Anything can be passed as an argument (what is relevant to the error - a string description, a numeric code, the object with which the error occurred, etc.)

Usually, after this function, the entire program crashes. And this is not always necessary. If you call a function that might throw an error (or its child functions might throw an error), then call it safely using pcall ():

Function f (x, y) ... if ... then error ("failed to do somthing") end ... end status, err = pcall (f, x, y) - f: function, xy: its arguments if not status then - handle error err. In our case, err contains the error text end

Standard Libraries

There are many non-standard libraries, they can be found on LuaForge, LuaRocks and other repositories.

Between Lua and Non-Lua

And if the functionality of the standard libraries is not enough for us? What if we have our C program, and we want to call its functions from Lua? There is a very simple mechanism for this.

Let's say we want to create our own function that returns a random number (Lua has math.random (), but we want to learn). We'll have to write the following code in C:

#include #include #include / * actually what to do when calling `rand (from, to)` * / static int librand_rand (lua_State * L) (int from, to; int x; from = lua_tonumber (L, 1); / * first parameter of the function * / to = lua_tonumber (L, 2); / * second parameter of the function * / x = rand ()% (to - from + 1) + from; lua_pushnumber (L, x); / * return value * / return 1; / * only return one argument * /) / * in Lua "rand" corresponds to our librand_rand () function * / static const luaL_reg R = (("rand", librand_rand), (NULL, NULL) / * end of list of exported functions * / ); / * called when the library is loaded * / LUALIB_API int luaopen_librand (lua_State * L) (luaL_openlib (L, "librand", R, 0); srand (time (NULL)); return 1; / * succeed * /)

Those. Lua provides us with functions for working with data types, for receiving function arguments and returning results. The functions are few and far between. We are now building our library as a dynamic library, and we can use the rand () function:

Random = require ("librand") - load the library print (random.rand (1, 100)) print (random.rand (0, 1))

What if we want to call Lua code from our programs? Then our programs should create a Lua virtual machine, in which Lua scripts will be executed. It's much easier:

#include "lua.h" #include "lauxlib.h" int main () (lua_State * L = lua_open (); // create a Lua virtual machine luaL_openlibs (L); // load the standard library luaL_dofile (L, "rand. lua "); // execute the script lua_close (L); // close Lua return 0;)

Everything.

You can now write in Lua. If you find out interesting points about Lua that could be reflected in the article - write!

This tutorial series that I have conceived will discuss the Lua programming language. I will try to make the presentation as accessible as possible for beginners, and I will focus on them. That is, experienced Lua coders, most likely, will not learn anything new from this (I am sure they will only find room for nagging and remarks, which, in fact, are even welcome on their part), but if you have no rich programming experience behind you , then I think you can handle something.

The whole series will not obey any system. The lessons will sequentially introduce a number of language constructs so that by the third or fourth lesson you can already write your programs. My goal is to push you to learn the language on your own, to help you feel it, and not to explain it from A to Z - if you want to master the language completely, read the reference manual (which, albeit badly, is translated into Russian: http: //www.lua .ru / doc /). The sooner you move from lessons "for dummies" on the web to study the guide, the better.

If something is not clear - be sure to ask a question in the comments, and I and other members will try to help you.

Lua is a popular, easy-to-learn, embeddable, interpreted, dynamically typed general-purpose programming language. No, you don't need to understand even half of the words in the previous sentence - the main thing is to know that it is popular and uncomplicated. By the way, it has earned its popularity due to its simplicity and small size of the distribution kit (about 150 kilobytes). Lua scripts are supported by a large number of applications, including games. World of Warcraft and S.T.A.L.K.E.R. use the Lua language. My favorite game engine will allow you to create a variety of games with ease using Lua. As you can see, Lua opens up a lot of horizons for you!

Before we start, you need to set up a programming environment: that is, find a program that accepts the Lua code you write and executes it: the interpreter. There are three options here:

1. Download the official Lua distribution from one of the sites that supply them.

Only the source code of the interpreter can be downloaded from the official Lua site. However, by examining http://lua.org/download.html in the Binaries section, you may find links to sites with executable files for Windows. One of them: . Download from there one of the archives (matching your platform: Win32 or Win64) and unpack it somewhere, preferably into a directory with a short path: like C: \ lua. From now on, I will assume that you are using Windows, and your interpreter is there.

Users of Linux-based operating systems are easier in this sense: they just need to use the package manager and install Lua from the repositories. On Debian and Ubuntu, this is done with apt-get install lua, and on Fedora, Red Hat, and derivatives, it is done with yum install lua. However, do not blindly trust me and consult your operating system manual to find out exactly how you do this.

2. Use an online interpreter.

Located at http://www.lua.org/demo.html. At first, it may be enough, but later, when we touch on modules, you will have to use the offline version. Using the online interpreter is very simple: enter your program in the window with the text and click the Run button. The program will be executed, the Output window will show the output of your program, as well as error reports, if any.

3. Use IDE.

For example ZeroBrane Studio: http://studio.zerobrane.com/. There are others - search the Internet.

There are now two slightly different versions of Lua in circulation: 5.1 and 5.2. I will focus on the most recent version - version 5.2, but I will definitely point out the important differences between it and 5.1, since the latter is also quite common. By the way, Lua 5.1 executes code one and a half times faster than Lua 5.2, so you know.

=== Lesson # 1 ===

So, let's begin. Create a main.lua file in a folder isolated from extraneous files and write in it:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
- main.lua -
print ("Hello world!")

Then run on the command line (do not forget to move to the directory with main.lua using the cd command):

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
> C: \ lua \ lua.exe main.lua

In response, the Lua interpreter will issue:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
Hello world!

In principle, this was to be expected. In the program, we called the print function. The print function takes an arbitrary number of parameters and prints them sequentially to the screen. In this example, we passed it the string (character string) "Hello world!" You can just as well pass as a parameter:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
print (8) - some decimal number
- will output: 8

Print (0xDEADBEEF) - hexadecimal number
- will output: 3735928559

Print ("0xDEADBEEF") is a string, not a number! Do you see the quotes?
- will output: 0xDEADBEEF

Print (1.35e-4) - floating point number (fractional number)
- Will output 0.000135. 1.35e-4 should be understood as "1.35 multiplied
- by ten to the minus fourth degree ", if anyone does not know.

Print ((198 * 99) -3 * 500 + 14/88) - expression
- Will display the value of the expression: 18102.159090909. Not a bad alternative
- a desktop calculator!

Print (198/7, "fertilizer", 2 ^ 9) - several parameters of an arbitrary
- type. The values ​​of each of them will be displayed, separated by signs
- tabs:
- 28.285714285714 fertilizer 512
- Please note that quotes around fertilizer are not displayed!

Print (1.35) is two numbers, not a decimal fraction of 1.35!
- A comma is used to separate parameters.
- Will output:
-- 1 35

The "-" sign is not just an imitation of a dash, which is inserted for beauty. The "-" sign in Lua denotes comments: hints for the programmer that are ignored by the interpreter and intended to make the code easier to understand. You can try to write in the program:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
- print ("nothing")

The interpreter will think it is a comment and will not follow the instruction.

Note to the hostess: if you want to print only one line, you can write the print call like this, without parentheses:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
print "Just one string"

Convenience is definitely questionable: just keep in mind that it is possible. However, such calls are unacceptable:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
print 2 - won't work, 2 - not a string.
print 2 * 2 + 6 - all the more will not work

Str = "string !!" - assigned to the variable str the value "string !!"
- read about variables below
print str won't work either.

In each of the above cases, the program will simply refuse to work. Thus, in a "parentless" call, a function name can only be followed by a string literal (that is, a sequence of characters enclosed in quotes), and nothing else. In the future, I will talk about this feature in a little more detail, but for now this is enough for you.

In any good programming language, it is possible to declare variables: small containers that can contain some data. Lua does it this way:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
<имя_переменной> = <выражение>

For instance:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
star = 8 - The variable star now stores the number 8
wars = "owl" - The wars variable contains the string "owl"
jedi = 42/2 - In variable jedi - number 21
luke = star * jedi - The luke variable is the number 168 (yes, 21 times 8)

The values ​​of variables and expressions with them can also be displayed:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
print (star, wars, jedi, jedi-star + luke)
- Will output:
- 8 owl 21 181

Just don't try to add the variables star and wars - trying to add 8 to "owl" won't do you any good!

As you should have noticed, the name of a variable can be almost anything: the main thing is that it does not start with a number. Seriously, you can even declare a variable named print, and then the print function will stop working, because the name print will refer to the newly declared variable. But there is a group of words that are forbidden to be used as variable names - these are language keywords that we have not yet met, but which are definitely worth looking at:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
and break do else elseif end
false for function goto if in
local nil not or repeat return
then true until while

By creating a variable with one of these names, you will cause an error in the program, and it will definitely not work. Please note that in Lua 5.1 there is no goto keyword, and you can name a variable that way, but you better not do that.
Also note that variable names are case sensitive. This means that foo, fOo, fOO and FOO are four different variables, so if you wrote the name of a variable in lowercase and later wrote it in uppercase, then most likely the program will not work correctly.

Now for one important point: what happens if you, by accident or on purpose, refer to a non-existent variable? In most other languages ​​this will cause an error, but in Lua this situation is acceptable. It is interpreted as if a non-existent variable actually exists, but its value is nil. nil- remember this word! is a special value type in Lua that means "nothing". Not a zero or an empty string (a string like "" - try to display it), but just nothing. Compare this with this model: there are two people, one of them has a bank account, but there is no money on it, and the other has no bank account at all. In Lua terms, it will be considered that the first has 0 dollars in the account, and the second has nil... And not even dollars, but just nil... I hope I haven't confused you.

Try, for example, to run a program like this:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
- main.lua -
foo = "bar"
print (foo, baz)
- Will output:
- bar nil

Thus, the variable baz, which does not exist but is assumed to be, has a value of nil, and the print function understands this and prints it to the screen as the string "nil". Lua has a good method for checking the existence of a variable: if the value of a variable is not nil, then it is at least declared. Alternatively, you can explicitly declare a variable to be nil:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
cool_var = nil

This can be done, and although it may seem silly at first glance, it is sometimes done. In subsequent lessons, you will learn who and why, and you will probably start doing the same. Sometimes, of course.
Be careful with nil: you can print nil, but you can't do arithmetic with it! That is, if you get away with print (nil), then a construct like 99 + nil will throw an error, even if you would like 99+ nil equaled 99. Believe me, I was also upset when I found out.

Summary:
1. We learned about the print function, what it can do and how to call it correctly without parentheses.
2. Learned how to declare variables, how to calculate expressions (albeit quite a bit), what the names of variables can be.
3. Learned about nil, imbued with its mystical mystery and gained confidence that in the future we will be associated with a lot of it.

For those who are inquisitive and who want to strengthen their knowledge, I offer simple exercises, which you can skip if you feel competent enough already:
1. Write a program that plays the chorus of your favorite song.
2. Try to output the values ​​of the following expressions. Try to understand why some of them work and some don't. Look at the errors that failed expressions cause.

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
2 + "string";
6 + "14";
"box" - "vox";
1 * "11b"
"148" * "1e6";


3. Write a program that exchanges two variables. That is:

200? "200px": "" + (this.scrollHeight + 5) + "px"); ">
a = 6502
b = 8086


Make a become equal to 8086, and b equal to 6502. To do this, create a third variable and perform some simple swaps. Make sure the problem is solved correctly by calling print (a, b) before the exchange and print (a, b) after.

Did you like the article? Share it