Lua basics. Introduction to Lua Lua sample programs

Lua basics. Introduction to Lua Lua sample programs

07.09.2021

Hello everyone.

Today we will take a quick tour of the Lua language, some of its features, as well as running our scripts in RakBot.
Lua is a scripting programming language designed for fast data processing. With the help of this language, many developers create artificial intelligence in games, write algorithms for generating levels, and it is also used to develop resources / game mods in Multi Theft Auto: San Andreas (analogue of SA: MP). In fact, this is the simplest language and with the help of it we will learn to write our own logic for bots that RakBot will use.

Let's go through the basics of programming with which we have to work.

note : This article will be truncated in terms of the Lua language, as only a small part of it is used in RakBot. Many Lua features are simply missing from RakBot, so I'll be targeting the RakBot version.

It is a tradition for all authors of books and documentation in various languages, this is the first program to print "Hello World".
Well, let's try to write it, but already in RakBot. Go to the official RakBot website and look for the "Available functions" section, the "Events" section.

We need an event onScriptStart (), which are called automatically when the script is loaded by RakBot itself.

In this function, we need to describe the logic that will write "a" Hello World "to the RakBot chat log. For this, on the same page in the documentation, look at the" Functions "section.

First function printLog (text)- this is what we need. With this function we will send a message to the RakBot chat "a. To do this, we will write:

We wrote the logic in some kind of text document, but how do we tell RakBot to execute our script? To do this, you need to save the file with the extension .lua and put it in a folder scripts, in the RakBot folder.
I saved a text document named " example.lua ". Let's try running RakBot and see what we get.

As we can see, when RakBot starts, it finds the script " example.lua", and then executes it. From this we can conclude that the script is initialized when RakBot itself is started or when all scripts are reloaded with the command ! reloadscripts.

Congratulations, you just wrote your own script for RakBot!

We have already learned how to write Hello World in the RakBot console, but we want to write complex bots that will do all the work for us, taking into account certain conditions.
Almost everything that happens in programming can be described as follows: take the data, do something with it, give the result.
In this case, the data is RakBot itself. He himself launches our scripts, as well as he himself transfers data to us, which we can process as we want and at the end get the result.

Let's write a simple conditional script. The condition will be the bot's nickname. If the bot's nickname is "СМaster", then we will display RakBot "and" CM FOREVER "in the chat, but if the bot's nickname is completely different, we will display" Nonamer "in the chat.
To do this, the conditional operator if else, aka the branch operator, will help us. It takes on a condition that must return either true or false. If the condition is true, then the code inside will be executed, if false - it will not be executed.
Most of the logic of any application is built on this. Literally if is translated as "IF", then - "MEANING", else - "OTHERWISE" If it is very difficult - do not worry, you will understand more and more.

Lua has the following comparison operators:
> More
< Меньше
> = Greater than or equal
<= Меньше или равно
~ = Not equal
== Equal

If we write " CMaster" == "CM"- we will have the value False, that is, false
If we write " CMaster" == "CMaster"- we will have the value True, that is, true.

5> 10 - false 5< 10 -- истина 10 ~= 15 -- истина 10 >= 5 - true

Let's try using the branching logic in our previous scenario.

The code that we wrote earlier:

Function onScriptStart () printLog ("Hello world!"); end

Let's transform as follows:

Function onScriptStart () botName = getNickName () if (botName == "CMaster") then printLog ("CM FOREVER"); else printLog ("Nonamer"); end end

Let's take a look at this code starting from the top. I advise you to start learning how to read code right away. Therefore, let's try to read what we got.

Function onScriptStart () - create a function named onScriptStart botName = getNickName () - write the bot name to the botName variable if (botName == "CMaster") then - if the bot name is "CMaster", then printLog ("CM FOREVER"); - write to the chat "CM Forever". else- ELSE, or if the bot name is NOT "CMaster" printLog ("Nonamer"); - write to chat "Nonamer" end - end of conditions end - end of function

Let's try to test the code we wrote. I saved the modified code under the name " example.lua "and launched RakBot with the nickname" Mason_Bennett".

After uploading our script, RakBot wrote to Nonamer chat. Let's try to log in with a nickname " CMaster".

As we can see, our condition works successfully and we see what we wanted in the chat.

Let's go over the variables a bit. You have a piece of paper and want to keep it. Save it in some way - put it somewhere so as not to lose it. For example, we can put our sheet of paper in a locker and take it out when we need it. If we have a new sheet and we do not need the old one, we will throw out the old sheet and put in a new one.
This is the logic of the variable. We can create a variable with the names we want and write the values ​​to them, which we did in the previous example with the botName variable.

In Lua, we can write whatever we want to a variable. For example, I want to create a variable named PaperList and write the text "Lua - lesson # 2" into it. For this I will write:

PaperList = "Lua - lesson number 1"

What have we done here? We wrote a name and used the "=" assignment operator and now I can use this variable anywhere in my script.
I think that if you remember mathematics at the level of a maximum of grade 5, everything will be clear here.

Lua has several types of variables, these are nil, boolean, number, string. Fear not, it's all very simple.

In fact, there are several more of them, but I have already said that most of the functionality in RakBot is missing.

nil - no value.
boolean - boolean values, accepts two variants of values ​​- either true or false.
number is a double precision real number. Lua does not have an integer type, so it acts as both a real and an integer type.
string is a string, here, I think, everything is clear.
Well, let's try creating some variables and playing around with them.

number = 0; - create a variable named number and assign the value 0
number = number + 5; - assigning the value to the variable number + 5 (that is, 0 + 5), now we have the number 5 stored here.
number ++; - ++ - increment. In other words, you take a variable and increment it by one. That is (5 + 1) - now 6 lies in the variable number.
number -; - - decrement. In other words, we decrease it by one unit. (6 - 1) - now the value is 5.

string = "Hello" - create a string variable with the value "Hello"
string = string .. "," - string concatenation, it is also string addition. What have we done here? Indicate the name of the variable, indicate the concatenation operator "..", and then indicate one more line to be added to the first. Now we have the value "Hello," in the "string" variable.
string = string .. getNickName() - now, to "Hello," we have added the bot's nickname, let it be "Michel". Now we have the value "Hello, Michel" in the string variable.

boolean = true; - create a boolean variable with the value true.
boolean = getNickName() == "Dimosha" - compare the bot name with the Dimosha string. Since the name of the bot we have is Michel, from the previous example, the value false will be written here.

A little about the functions. There are functions that return values, and there are those that do not. As you may have noticed, our function onScriptStart does not return a value, but simply executes the code that is specified inside.
We can create our own functions in order to isolate some of the logic from the method and perform certain operations.
Functions may or may not take on values.

Let's walk along the simplest path: a function with no parameters and no return value, which will add 5 + 10 and output the result to the RakBot console. "

I'll create a function named Add:

Function Add () - Create function Add printLog (5 + 10) - use RakBot method to output to the console end - End of function

We have created a not quite universal function for two reasons:
- if I need to add other numbers, I will have to create another one of the same function
- I cannot use the received value outside of the function

Let's try to fix the first defect. To do this, we will add two parameters to the function, which will take over the numbers for addition. We will do it as follows:

Function Add (a, b) printLog (5 + 10) end

Now, in the method, we have two values ​​available, which are contained in two new variables a and b, but I still display 15 on the console. Let's fix this:

Function Add (a, b) printLog (a + b) end

Ideally. Now, when we call this method, we will get the result of the addition in the console. Let's try to test it. Let's change our code to example.lua to the next:

Function Add (a, b) printLog (a + b) end function onScriptStart () Add (5, 10); Add (123, 4324); Add (555, 111); end

And let's try to launch RakBot. Let's see what happens:

This solved our first problem. Let's try to solve the second, so that our function returns a result.

Let's rewrite the Add function:

Function Add (a, b) return a + b end

return is a keyword for returning a value from a function. Now let's rewrite the onScriptStart method:

Function onScriptStart () printLog ("First value:" ..Add (5, 10)); printLog ("Second value:" ..Add (123, 4324)); printLog ("Third value:" ..Add (555, 111)); end

Let's see what happened.

We could create three variables by assigning values ​​from functions Add and then pass them to the method printLog, but I didn’t do it, as the code looks more readable and nicer.

Now, we have learned how to create our own functions with parameters, without parameters, and return values ​​from them. I believe that these basics will be enough for you to write your own bot within the framework of RakBot "a. In the next lessons we will create a simple bot, which we will gradually complicate, adding more and more new features and functions.

Introduction

This guide is intended for those with limited experience with LUA. We'll cover the basics of how to code out, building blocks for you to create more complex code, and provide some examples. The manual is written so that you can immediately apply it in practice. Therefore, you should open Tabletop Simulator and your LUA editor to follow along.

This is the first tutorial in this series. The second is Learning Lua More. The third is a collection of useful functions called Learning Lua Functions.

Before the first key press

First, I would highly recommend installing Atom if you are going to script in Tabletop Simulator. He knows what functions can be used and will import / export code to / from TTS.

Then you have to add to bookmarks. You will link to this site often once you start writing your scripts. Here you will find special features in Tabletop Simulator and how they work. You will most often be using API and Object pages, at least in my experience.

Preparation

When you save your scripts to Tabletop, it will use your last save and then load the scripts into it. Therefore, for any script that you intend to write, you will need to do the following:

  • Prepare the table the way you want it.
  • Save the table.
  • Load the table.
For this exercise, take an empty table and create two objects (I used a square block and a rectangular block) and a red checker.

Press it again because of course you will.

EXTRA CREDIT: When you create tables, there are several ways to do this. The method used here was to provide visual clarity. However, a way to create button parameters like this takes up a lot of space if you have a lot of buttons. I prefer to design my tables in such a way as to save space, but not go over the right margin. Using our example, I would create a parameter table like this:

button_parameters = (click_function = "buttonClicked", function_owner = nil, label = "Press Me", position = (0,0.8,0), rotation = (0,0,0), width = 500, height = 500, font_size = 100 )

EXTRA CREDIT: This is the perfect time to start playing with the different things you can do with objects. Go to the Object page in the Knowledge Base and try out the material. Move objects, make them switch positions, change their colors, whatever you think.

EXTRA CREDIT: In addition, each time the button is pressed, the click_function is run with two parameters. The first is a reference to an object, in particular a reference to the object to which the button is bound. The second is the color (for example, "Blue" - blue) in line format, the color of the player who pressed the button.

5) Boolean assertion

Variable comparison

Again remove all scripts inside the buttonClicked () function. We're going to create a new variable and then modify it. The new variable will be of Boolean type. Boolean values ​​can only be true, false. Boolean values ​​are always written in small letters. First, we will create our variable under our objects and checkers GUID.

trueOrFalse = true

Then, in buttonClicked, we'll set up some logic to check if trueOrFalse is true. If it is True, it will print that it is Truth and switch it to False. If the button is pressed again, it will print that it is False and switch the value to True.

if trueOrFalse then print ("trueOrFalse was true.") --trueOrFalse was true. trueOrFalse = false else print ("trueOrFalse was false.") --trueOrFalse was false. trueOrFalse = true end

We could also write it like "if trueOrFalse == true then", but this is optional. Remember that the IF statement needs to be passed a boolean value. And since trueOrFalse is already one of those, we can let go of "== true".

A loop is a section of code that can run multiple times. This is one of the more complex elements that you will use in LUA. They often come with tables, allowing you to run code for every record in the table.

This is another type - ipairs. Pairs are needed for tables with no numeric keys, and ipairs are needed for tables with sequential numeric keys (arrays). ipairs comes in order, where pairs can go in any order.

Lua gives you the power; you build the mechanisms.
// Roberto Ierusalimsky


Introduction

Lua is a programming language designed to be embedded in other applications to enable their users to write configuration scripts and high-level scripts. Lua supports procedural, object and functional programming styles, but is also a simple language. The Lua interpreter is written in ANSI-C and is a library that can be plugged into any program. In this case, the control program can call library functions to execute a piece of Lua code and work with the data defined in this code. Also, the control program can register its own functions so that they can be called from Lua code. The latter feature allows Lua to be used as a language that can be adapted to an arbitrary field of use. Another use for Lua is writing simple, independent scripts. For this purpose, there is a simple Lua interpreter that uses this library to execute code entered from the console or from a file.

Lexical conventions

Identifiers can contain letters, numbers, and underscores, and cannot start with a number.

Identifiers that start with an underscore and contain only uppercase letters are reserved for internal use by the interpreter.

Identifiers distinguish between upper and lower case letters.

String literals can be enclosed in single or double quotes. They can use the following special character sequences:

\ n line feed (LF = 0x0a) \ a bell \ r carriage return (CR = 0x0d) \ b backspace \ t tab \ f form feed \\ backslash character \ v vertical tab \ "quote \ [left square bracket \ "apostrophe \] right square bracket \ ddd character with code ddd (decimal) \ 0 character with code 0

If there is a backslash at the end of a line in the source file, the definition of a string literal can be continued on the next line, in which a newline character is inserted at this point.

String literals can also be enclosed in double square brackets [[....]]. In this case, the literal can be defined on multiple lines (line feeds are included in the string literal) and does not interpret special character sequences.

If there is a line feed immediately after the "[[" characters, then it is not included in the string literal.

In addition to double square brackets, the character [=== [....] ===] can be used as line delimiters, in which an arbitrary number of equal signs are located between the repeated square brackets (the same for the opening and closing delimiters).

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 hex using the 0x prefix.

The comment begins with "-" (two minus characters in a row) and continues to the end of the line. If immediately after the symbols "-" there are symbols "[[", then the comment is multi-line and continues until the symbols "]]". A multi-line comment can contain nested [[....]] character pairs. In addition to double square brackets, the character [=== [....] ===] can also be used as delimiters for multi-line comments, in which an arbitrary number of equal signs are located between the repeated square brackets (the same for the opening and closing delimiters). The string constant escapes comment start characters.

If the first line of the file begins with the "#" character, then it is skipped. This allows Lua to be used as a script interpreter on Unix-like systems.

Data types

Lua provides the following data types:

Nil empty boolean boolean number numeric string string function userdata function user data thread thread table associative array

The nil type means that a variable has no value. This type has a single nil value.

The boolean type has two meanings: true and false.

A nil value is considered false. All other values, including the number 0 and the empty string, are considered Boolean true.

All numbers are represented as double precision real numbers.

Strings are arrays of 8-bit characters and can contain a zero-code character inside them. All strings in Lua are constant i.e. you cannot change the content of an existing string.

Functions can be assigned to variables, passed to functions as an argument, returned as a result from a function, and stored in tables.

The userdata type corresponds to an untyped pointer at which arbitrary data can be located. A Lua program cannot directly work with such data (create, modify them). This data type does not correspond to any predefined operations other than assignment and comparison for equality. At the same time, such operations can be defined using the mechanism of metamethods.

The thread type corresponds to an independently executable thread. This data type is used by the coroutine engine.

The table type corresponds to tables - associative arrays that can be indexed by any value and which can simultaneously contain values ​​of arbitrary types.

The type of the object stored in the variable can be found out by calling the type () function. This function returns a string containing the canonical name of the type: "nil", "number", "string", "boolean", "table", "function", "thread", "userdata".

Conversions between numbers and strings happen automatically when they are used in the appropriate context.

Arithmetic operations involve numeric arguments, and trying to do this on strings will convert them to numbers. String operations performed on numbers lead to their conversion to a string using some fixed format conversion.

You can also explicitly convert an object to a string using the tostring () function, or to a number using the tonumber () function. For more control over the conversion of numbers to strings, use the format conversion function.

Variables

In Lua, variables do not need to be declared. The variable appears when it is first used. If a variable is used that has not been previously initialized, then it is nil. Variables do not have a static type, the type of a variable is determined by its current value.

A variable is considered global if it is not explicitly declared as local. Local variable declarations can be located anywhere in the block and can be combined with their initialization:

Local x, y, z local a, b, c = 1, 2, 3 local x = x

When a local variable is initialized to the right of the equal sign, the input variable is not yet available and the value of the variable external to the current block is used. This is why the example on the third line is correct (it demonstrates a commonly used idiom in the language).

For local variables, the interpreter uses lexical scopes i.e. the scope of a variable extends from the point of its declaration (first use) to the end of the current block. In this case, the local variable is visible in blocks that are internal to the block in which it is described. The local variable disappears when it goes out of scope. If a local variable is defined outside the block, then such a variable disappears at the end of the execution of this section of the code, since the section of the code is executed by the interpreter as an unnamed function. An initialized global variable exists for the entire duration of the interpreter's operation.

To remove a variable, you can simply assign the value nil to it.

Arrays, functions and userdata are objects. All objects are anonymous and cannot be the value of a variable.

Variables store references to 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.

Tables

Tables (type table) correspond to associative arrays that can be indexed by any value other than nil and which can simultaneously contain values ​​of arbitrary types other than nil. Table elements can also be indexed by objects - tables, functions, and userdata objects. Array elements that are not assigned values ​​are nil by default.

Tables are the main data structure in Lua. They also represent structures, classes and objects. In this case, indexing by the string name of the structure field is used. Since an array element can be a function, methods are also allowed in structures.

Arrays are indexed using square brackets: array. The struct.field entry is equivalent to the following entry: struct ["field"]. This syntactic feature allows tables to be used as named field records.

A table constructor is an expression that creates and returns a new table. Each run of the constructor creates a new table. The table constructor is a list of field initializers (possibly empty) enclosed in curly braces, separated by a comma or ";" (semicolon). The following options are valid for field initializers:

Exp2 table = exp2 name = exp table ["name"] = exp exp table [j] = exp

In the latter case, the variable j runs through consecutive integer values, starting from 1. The first two initializers do not change the value of this counter. Here's an example of constructing a table:

X = (len = 12, 11, 12, = 1123)

After executing such an operator, the table fields will receive the following values:

X ["len"] = x.len = 12 x = 11 x = 12 x = 1123

If the last element of the initializer list is a function call, then the values ​​returned by the function are sequentially placed in the initializer list. This behavior can be changed by enclosing the function call in parentheses. In this case, of all the values ​​returned by the function, only the first is used.

The last initializer may be followed by an optional field initializer separator character (comma or semicolon).

Operations

The basic operations are listed below:

Change sign + - * / arithmetic ^ exponentiation == ~ = equality< <= >> = order not and or logic .. string concatenation # get string or array length

When using arithmetic operations, strings that have a numeric value are converted to it. When concatenating numeric values, they are automatically converted to strings.

Comparison for equality does not perform type conversion. Objects of different types are always considered different.

Accordingly, "0" ~ = 0, and when indexing, a and a ["0"] correspond to different cells of the array. When comparing for equality / inequality of objects, comparison of references to objects is performed. Variables referring to the same object are equal.

When figuring out the order, the types of the arguments must be the same, i.e. numbers are compared to numbers, and strings are compared to strings.

Equality and order relationships always result in true or false i.e. boolean value.

In logical operations, nil is treated as false, and all other values, including zero and the empty string, are treated as true.

When calculating the value, a short circuit is used - the second argument is calculated only if necessary.

The following table of priorities and associativity of operations applies:

^ not # - (unary) * / + -< > <= >= ~ = == .. and or

Logic operations and related idioms

The not operator always returns a boolean value, accepting an argument of an arbitrary type (only the nil value corresponds to the boolean value false, the rest are treated as true). In contrast, the and and or operators always return one of their arguments. The or operator returns its first argument if its value is other than false and nil, and its second argument otherwise. The and operator returns its first argument if its value is false or nil and its second argument otherwise. This behavior is based on the fact that all non-nil values ​​are treated as true.

Several common idioms are associated with this behavior. The following table shows the idiomatic operation on the left and the equivalent normal notation on the right:

X = x or v if x == nil then x = v end x = (e and a) or b if e ~ = nil then x = a else x = b end

The first idiom is often used to assign a default value to an uninitialized variable. The second idiom is equivalent to the C "operator x = e? A, b (here the value of the variable a is assumed to be non-nil).

Operators

Lua does not have a dedicated function to start program execution with. The interpreter sequentially executes the statements that it receives from a file or from a control program. In doing so, it precompiles the program into a binary representation, which can also be saved. Any block of code is executed as an anonymous function, so local variables can be defined in it and a value can be returned from it.

Operators can (but not necessarily) be separated by ";" ...

Multiple assignments are allowed:

Var1, var2 = val1, val2

In this case, the alignment is performed - the extra values ​​are discarded, and the variables corresponding to the missing values ​​are assigned the nil value. All expressions on the right side of the multiple assignment are evaluated before the assignment itself.

If the function returns several values, then using multiple assignment, you can get the return values:

X, y, z = f (); a, b, c, d = 5, f ();

In general, if a function call is at the end of the list of values ​​to the right of the assignment sign, then all values ​​returned by the function are appended to the end of the list of values. This behavior can be changed by enclosing the function call in parentheses. In this case, of all the values ​​returned by the function, only the first is used.

The main operators are listed below:

Do ... end if ... then ... end if ... then ... else ... end if ... then ... elseif ... then ... end if ... then. .. elseif ... then ... else ... end while ... do ... end repeat ... until ... for var = start, stop do ... end for var = start, stop, step do ... end return return ... break

The do ... end block turns a sequence of statements into a single statement and opens up a new scope in which you can define local variables.

In the if, while, and repeat statements, all expression values ​​other than false and nil are interpreted as true.

Here is the general form of the if statement:

If ... then ... (elseif ... then ...) end

The return statement can contain no return values ​​or contain one or more expressions (list). Since a block of code is executed as an anonymous function, the return value can be not only for the function, but also for an arbitrary block of code.

The return and break statements must be the last statements in the block (that is, they must be either the last statements in the code block, or placed immediately before the words end, else, elseif, until). Inside the block, you must use the do return end or do break end idiom.

The for statement is executed for all values ​​of the loop variable from the start value to the end value, inclusive. The third value, if specified, is used as the step of modifying the loop variable. All of these values ​​must be numeric. They are evaluated only once before executing the loop. The loop variable is local to this loop and is not available outside of its body. The value of a loop variable cannot be changed inside the loop body. Here is pseudocode that demonstrates the execution of the for statement:

Do local var, _limit, _step = tonumber (start), tonumber (stop), tonumber (step) if not (var and _limit and _step) then error () end while (_step> 0 and var<=_limit) or (_step<=0 and var>= _limit) do ... var = var + _step end end

Functions

A function definition is an executable expression (function constructor) that evaluates to an object of type function:

F = function (...) ... end

The list of (possibly empty) function arguments is placed in brackets. The function argument list can end with an ellipsis - in this case, the function has a variable number of arguments. The body of the function is placed between the closing parenthesis and the end statement.

The following short forms are available for defining a function:

Function fname (...) ... end fname = function (...) ... end local function fname (...) ... end local fname = function (...) ... end function x .fname (...) ... end x.fname = function (...) ... end function x: fname (...) ... end x.fname = function (self, ...) ... end

At the time of execution of the function constructor, it also builds closure- a table of all local variables available in the function and external to it. If a function is passed as a return value, then it retains access to all the variables included in its closure. Each time the function constructor is executed, a new closure is built.

A function call consists of a function reference and a parenthesized (possibly empty) argument list. A function reference can be any expression that evaluates to a function. There cannot be a line feed between the function reference and the opening parenthesis.

The following short forms are available for calling functions:

F (...) f ((...)) f ("...") f "..." f ("") f "" f ([[...]]) f [[. ..]] x: f (...) xf (x, ...)

In the first case, the only argument is an on-the-fly table, and in the next three it is a string literal. In the latter case, x is used as a table from which the variable f is retrieved, which is a reference to the called function, and the same table is passed to the function as the first argument. This syntactic feature is used to implement objects.

When a function is called, values ​​of simple types are copied to arguments by value, and for objects, references are copied to arguments. When the function is called, the number of arguments is aligned - the extra values ​​are discarded, and the arguments corresponding to the missing values ​​are nil. If at the end of the parameter list there is a call to a function that returns several values, then all of them are added to the argument list. This behavior can be changed by enclosing the function call in parentheses. In this case, of all the values ​​returned by the function, only the first is used.

If the function has a variable number of arguments, then values ​​that are not assigned to any of the arguments can be obtained using a specific name .... This name behaves like a set of values ​​returned by a function i.e. when it appears inside an expression or from the middle of a list of values, only the first return value is used. If the name ... appears in the last position of the list of values ​​(in table constructors, with multiple assignments, in the list of arguments when calling a function, and in the return statement), then all returned values ​​are placed at the end of this list (the rule for completing lists). To suppress this behavior, the name ... can be enclosed in parentheses. You can turn a set of values ​​into a list using the (...) idiom.

If the function takes a single argument and treats it as a table, whose elements are indexed by the names of the formal parameters of the function, then in this case the call to the mechanism of named arguments is actually implemented:

Function rename (arg) arg.new = arg.new or arg.old .. ".bak" return os.rename (arg.old, arg.new) end rename (old = "asd.qwe")

The return from the function occurs both when the execution of its body is completed and when the return statement is executed. The return statement can return one or more values. If at the end of the list of return values ​​there is a call to a function that returns several values, then all of them are added to the list of arguments. This behavior can be changed by enclosing the function call in parentheses. In this case, of all the values ​​returned by the function, only the first is used.

If the function is called as a statement, then all return values ​​are destroyed. If the function is called from an expression or from the middle of a list of values, only the first return value is used. If the function is called from the last position of the list of values ​​(in table constructors, in multiple assignments, in the argument list when calling the function, and in the return statement), then all returned values ​​are placed at the end of this list (the rule for completing lists). To suppress this behavior, a function call can be enclosed in parentheses.

There is a predefined function unpack () that takes an array whose elements are indexed from 1 and returns all of its elements. This function can be used to call functions that take a variable number of arguments, dynamically generating a list of actual arguments. The reverse operation is performed as follows:

List1 = (f ()) - create a list of all values ​​returned by the f () function list2 = (...) - create a list of all values ​​passed to the function with a variable number of arguments

The variable number of return values, the rule for completing lists, and the ability to pass to a function, not all formal parameters can interact in a non-trivial way. Often a function (like foo ()) will return a response on normal completion and nil and an error message on abnormal completion. The assert (val, msg) function generates an error with the message message (by calling the error (msg) function) if val is false or nil and returns val otherwise. Then the operator

V = assert (foo (), "message")

if successful, assigns the value returned by foo () to v. In this case, foo () returns a single value, and assert () receives a nil msg parameter. On error, the assert () function gets nil and an error message.

Iterators

Iterators are used to enumerate the elements of arbitrary sequences:

For v_1, v_2, ..., v_n in explist do ... end

The number of variables in the list v_1, ..., v_n can be arbitrary and does not have to match the number of expressions in the explist list. The explist is usually a call to the iterator factory function. Such a function returns an iterator function, state and initial value of the loop control variable. The iterator is interpreted as follows:

Do local f, s, v_1 ​​= explist local v_2, ..., v_n while true do v_1, ..., v_n = f (s, v_1) if v_1 == nil then break end ... end end

At each step, the values ​​of all variables v_k are calculated by calling the iterator function. The value of the control variable v_1 controls the end of the loop - the loop ends as soon as the iterator function returns nil as the value for the variable var_1.

In fact, the variable v_1 controls the iterations, and the rest of the variables can be considered as the "tail" returned by the iterator function:

Do local f, s, v = explist while true do v = f (s, v) if v == nil then break end ... end end

The operator in which the factory function is called is interpreted as a normal assignment operator i.e. a factory function can return any number of values.

Stateless iterators

A stateless iterator does not store any internal information that allows it to determine its position in the iterable container. The next value of the control variable is calculated directly from its previous value, and the state is used to store a reference to the iterable container. Here's an example of a simple, stateless iterator:

Function iter (a, i) i = i + 1 local v = a [i] if v then return i, v else return nil end end function ipairs (a) return iter, a, 0 end

Iterators storing state in a closure

If an iterator needs internal state to traverse a container, the easiest way is to store it in a closure created from the context of the factory function. Here's a simple example:

Function ipairs (a) local i = 0 local t = a local function iter () i = i + 1 local v = t [i] if v then return i, v else return nil end end return iter end

Here, the iterator stores the entire context in a closure and does not need the state and current value of the manipulated variable. Accordingly, the iterator does not accept the state and control variable, and the factory does not return the state value and the starting value of the control variable.

Standard iterators

The most common use of iterators is to traverse table elements. There are several predefined iterator factory functions for this. The pairs (t) factory returns an iterator giving at each step an index in the table and a value allocated at this index:

For idx, val in pairs (tbl) do ... end

In fact, this iterator is easy to define using the standard next (tbl, idx) function:

Function pairs (tbl) return next, tbl, nil end

The next (tbl, idx) function returns the next index value after idx after some traversal of the tbl table (calling next (tbl, nil) returns the initial index value; nil is returned when the table elements are exhausted).

The ipairs (tbl) factory returns an iterator that works exactly like the one described above, but for traversing tables indexed with integers starting at 1.

Meta tables

Each table and object of the userdata type can have a meta-table - a regular table, the fields of which determine the behavior of the original object when certain special operations are applied to it. For example, when an object turns out to be an operand in addition, the interpreter looks in the meta-table for a field named __add and, if such a field is present, then uses its value as a function that performs the addition. Meta tables allow you to define the behavior of an object during arithmetic operations, comparisons, concatenation, and indexing. You can also define a function to be called when an object of type userdata is released. The indices (field names) in the meta table are called events and the corresponding values ​​(event handlers) are metamethods.

By default, the newly created table does not have a meta table. Any table mt can be made a meta-table of table t by calling the setmetatable (t, mt) function. The getmetatable (t) function returns the meta table of the table t or nil if the table does not have a meta table. Any table can act as a meta-table for any other table, including itself.

Lua defines the following events:

Add, __sub, __mul, __div arithmetic operations __pow exponentiation __unm unary minus __concat concatenation __eq, __lt, __le comparison operations __index access by missing index __newindex assignment to new table element __call table call to __tostring function

The expression a ~ = b evaluates to not (a == b). The expression a> b evaluates as b< a . Выражение a >= b is calculated as b<= a . При отсутствии метаметода __le операция <= вычисляется как not (b < a) т.е. с помощью метаметода __lt .

For binary operations, the handler is selected as follows: the first operand is polled and, if it does not define a handler, then the second operand is polled. For comparison operations, a metamethod is selected only if the operands being compared are of the same type and the same metamethod to perform that operation. The user manual provides pseudo-code in Lua that demonstrates the context of calling metamethods.

The __index event handler can be a function or a table. In the case of a function, the handler is called and the table and index value is passed to it. Such a function should return the indexing result. In the case of a table, the table is re-indexed with the same index. If the __newindex event handler is present, then it is called instead of assigning a value to a new table element. If this handler is a table, then the assignment is made to that table.

The __tostring metamethod allows you to handle the conversion of an object (table or userdata) to a string. The __metatable metamethod allows you to handle the operation of getting a meta table. If this field in the meta-table has a value, then the getmetatable () function will return the value of this field, and the setmetatable () function will fail.

Examples of using meta tables

Default value for table fields

The following example uses meta tables to assign a default value for missing table elements:

Function set_def (t, v) local mt = (__index = function () return v end) setmetatable (t, mt) end

Here's a more nontrivial solution that doesn't use a separate meta table for each default value:

Local key = () local mt = (__index = function (t) return t end) function set_def (t, v) t = v setmetatable (t, mt) end

Here, the local (empty) key table is used as a known unique index at which the default value v is stored in the original table t. All tables for which the default value of missing fields is set share a common meta-table mt. The __index meta-method of this meta-table intercepts calls to the missing table fields and returns the value stored in the table itself at the key index.

This solution has a drawback: a new key-value pair appears in the table, which will appear when you try to traverse all the elements of the table.

Proxy table

In the following example, an empty table acts as a proxy for forwarding calls to table fields:

Local key = () local mt = (__index = function (t, k) return t [k] end, __newindex = function (t, k, v) t [k] = v end) function proxy (t) local proxy = () proxy = t setmetatable (proxy, mt) return proxy end

Here, the local (empty) key table is used as a deliberately unique index by which a reference to the original table t is stored in the proxy table. A proxy is a table, the only element of which has the key index, so accessing any element of the proxy will result in a call to the meta-method. The meta-table common to all proxies defines the __index and __newindex meta-methods, which retrieve the original table from a single proxy element, indexing it with the key table.

Meta-methods can provide arbitrary discipline of processing calls to the fields of the source table. Simple examples are logging hits or generating an error when trying to change the value of a table item.

Weak tables

If an object was used as an index on a table, or a reference to it was stored in the table, then the garbage collector will not be able to reclaim the object. At the same time, in some cases it is desirable to have a table in which the relationship of its elements with keys and / or values ​​is "weak", i.e. not interfering with garbage collection. Typically, such a need arises when caching the results of calculations in a table and storing object attributes in a table indexed by the objects themselves. In the first case, a weak link is desirable for values, in the second, for indices.

The relationship of table elements with objects (values ​​and indices) is determined by the value of the __mode string field of its meta-table. If this field contains the character "k", then the link for the indices (keys) is weak; if it contains the character "v", then the linkage for the values ​​is made weak. The field can contain both characters, which makes loose coupling for both indices and values.

If we use weak tables, then for the above problem of matching the table of the default value for the missing fields, the following solution can be given:

Local defaults = () setmetatable (defaults, (__mode = "k")) local mt = (__index = function (t) return defaults [t] end) function set_def (t, d) defaults [t] = d setmetatable (t , mt) end

Here is another solution, where weak tables stores meta tables, the number of which is the same as the number of different default values:

Local metas = () setmetatable (metas, (__mode = "v")) function set_def (t, d) local mt = metas [d] if mt == nil then mt = (__index = function () return d end) metas [d] = mt end setmetatable (t, mt) end

Global context

All global variables are fields of a regular table called global context... This table is accessible through the global variable _G. Since all global variables are context fields, _G._G == _G.

The global context makes it possible to access global variables by a dynamically generated name:

Val = _G _G = val

Since the global context is a regular table, a meta table can correspond to it. The following example injects environment variables into the global scope as read-only global variables:

Local f = function (t, i) return os.getenv (i) end setmetatable (_G, (__index = f))

The same technique allows you to deny access to uninitialized global variables.

Packages

Packages are the main way to define a set of related functionality without polluting the global scope. Typically, a package is a separate file that, in the global scope, defines a single table that contains all the functions of that package:

My_package = () function my_package.foo () ... end

You can also make all functions local and separately form a table of exported functions:

Local function foo () ... end local function bar () ... end my_package = (foo = foo, bar = bar,)

The package is loaded using the require () function, and at load time, the name passed to this function (it may not contain an extension that is added automatically) is available through the _REQUIREDNAME variable:

If _REQUIREDNAME == nil then run_some_internal_tests () end

Classes and objects

The tbl: func () construct (when declaring a function and when calling it) provides the basic functionality that allows you to work with a table as an object. The main problem is spawning many objects with similar behavior i.e. spawned from one class:

Function class () cl = () cl .__ index = cl - cl will be used as a meta-table return cl end function object (cl, obj) obj = obj or () - there may already be fields filled setmetatable (obj, cl ) return obj end

Here the class function creates an empty table, prepared to become the meta-table of the object. Class methods are made by fields of this table i.e. a class is a table that contains both the object's methods and its meta-methods. The object () function creates an object of the specified class - a table that has the specified class as its meta-table. The second argument can be a table containing the initialized fields of the object.

Some_Class = class () function Some_Class: foo () ... end function Some_Class: new () return object (self, (xxx = 12)) end x = Some_Class: new () x: foo ()

Inheritance

In the described implementation, the meta-table of the class remains unused, which makes it easy to implement inheritance. The inheriting class is created as an object of the class, after which it sets the __index field in such a way that it can be used as a meta-table:

Function subclass (pcl) cl = pcl: new () - instantiate cl .__ index = cl - and make it a class return cl end

Now you can add new fields and methods to the resulting inherited class:

Der_Class = subclass (Some_Class) function Der_Class: new () local obj = object (self, Some_Class: new ()) obj.yyy = 13 - add new fields return obj end function Der_Class: bar () ... end - and new methods y = Der_Class: new () y: foo () y: bar ()

The only non-trivial point here is to use the new () function from the ancestor class and then replace the meta table by calling the object () function.

When accessing the methods of the object of the inheriting class, first of all, they are searched for in the meta-table, i.e. in the inheritor class itself. If the method was inherited, then this search will be unsuccessful and the meta-table of the inheriting class will be accessed, i.e. to the ancestor class.

The main disadvantage of the given general solution is the impossibility of passing parameters to the new () function of the ancestor class.

Command line arguments

Command line arguments passed at startup are available as elements of the arg array.

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 the underscore character that does 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

The global variable exists as long as the script execution environment exists and is available to any Lua code executed in this 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 access to local variables in Lua is somewhat faster than to global ones).

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, that is, the string is created entirely 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 themselves, 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 equals "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 there are two opening square brackets ([[) immediately after the "-" characters, the comment is multi-line 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.

Lua allows the following comparison operations for values:

== (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 (and 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 example:

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. Read more in the article "Working with arrays in Lua".

Operation precedence in Lua

In the Lua language, 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 table _G:

local a = _G.var

Operator (instruction) 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 the truth of the given condition. 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

Loop with precondition (while) in Lua

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 has been executed, therefore, in any case, the loop body will be executed at least once.

Sum the values ​​of 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 peculiarities 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 a step of 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 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 the f () function

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 - save 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 is it?

Lua is a simple embeddable language (it can be integrated with your programs written in other languages), lightweight and straightforward, with a single 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 imperative and object-oriented or functional style (even if you don't know how it is - no big deal, 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 embeddable 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 (this, by the way, 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, the keys can be 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 table fields. 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, and 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 actions 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 what kind of light bulb it needs to work with in order to change the on / off state. But in order not to be verbose, Lua has an abbreviation 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 dot, 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 result of comparison (<, <=, ==)
* __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) - we 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 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!

© 2021 hecc.ru - Computer technology news