Вы находитесь на странице: 1из 97

Lua

#lua
1

1: Lua 2

Examples 4

Lua 6

nil 9

10

10

10

10

10

11

11

12

12

( !) 12

13

, 13

2: Pico-8 15

15

Examples 15

15

16

17

3: Lua 18
18

Examples 18

18

18

18

19

19

20

20

20

21

22

«» « ». 22

22

/ 23

23

23

23

23

4: 25

25

25

25

Examples 26

26

27

5: API Lua C 30

30

30

Examples 30

Lua 30
Lua 31

Lua API Lua 32

33

: 34

: 34

: 34

6: 36

Examples 36

36

36

37

37

Ipairs 37

37

38

38

38

39

Coroutines 39

7: 41

41

41

41

Examples 41

41

42

- __gc 42

43

43

44

44

45
45

46

8: 48

Examples 48

48

48

48

48

49

49

49

9: 50

Examples 50

pcall 50

Lua 51

10: - 53

53

53

Examples 53

53

54

11: 56

56

56

Examples 56

56

12: 57

57

58

Examples 58

Lua 58
string.find () 60

find 60

60

`gmatch` 61

61

: 62

gsub 62

62

62

62

63

13: 64

64

Examples 64

64

65

14: 66

66

66

Examples 66

66

15: 70

70

70

Examples 71

71

71

73

, 75

75

? 77
78

16: 80

80

80

Examples 80

80

. 81

82

82

82

- 83

83

84

85

85

86

87

88

88

89
Около
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: lua

It is an unofficial and free Lua ebook created for educational purposes. All the content is extracted
from Stack Overflow Documentation, which is written by many hardworking individuals at Stack
Overflow. It is neither affiliated with Stack Overflow nor official Lua.

The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.

Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to info@zzzprojects.com

https://riptutorial.com/ru/home 1
глава 1: Начало работы с Lua
замечания

Lua - минималистичный, легкий и встраиваемый язык сценариев. Он разрабатывается,


внедряется и поддерживается командой в PUC-Rio , Папском католическом университете
Рио-де-Жанейро в Бразилии. Список рассылки открыт для участия.

Обычные прецеденты для Lua включают в себя скриптовые видеоигры, расширение


приложений с помощью плагинов и конфигов, перенос некоторых бизнес-логик высокого
уровня или просто внедрение в такие устройства, как телевизоры, автомобили и т. Д.

Для высокопроизводительных задач существует независимая реализация с


использованием доступного во времени компилятора LuaJIT .

Версии

Дата
Версия Заметки
выхода

1993-07-
1,0 Первоначальный, непубличный выпуск.
28

1994-07-
1,1 Первый публичный выпуск. В документе описывается это.
08

Начиная с Lua 2.1, Lua стал свободно доступным для всех целей,
1995-02-
2,1 включая коммерческое использование. Журнал, описывающий
07
его.

1995-11-
2,2 Длинные строки, интерфейс отладки, лучшие трассировки стека
28

1996-05-
2,4 Внешний компилятор luac
14

2.5 Совмещение шаблонов и функции vararg. 1996-11-

https://riptutorial.com/ru/home 2
Дата
Версия Заметки
выхода

19

Введена auxlib, библиотека для помощи в написании библиотек 1997-07-


3.0
Lua 01

1998-07-
3,1 Анонимные функции и закрытие функций с помощью «upvalues».
11

1999-07-
3,2 Библиотека отладки и новые функции таблицы
08

2000-02-
3.2.2
22

2000-11-
4,0 Несколько состояний, «для» операторов, API revamp.
06

2002-07-
4.0.1
04

Coroutines, metatables, полный лексический охват, tail calls, 2003-04-


5.0
booleans переходят на лицензию MIT. 11

2006-06-
5.0.3
26

Обновление системы модулей, инкрементный сборщик мусора,


2006-02-
5,1 метаданные для всех типов, luaconf.h revamp, полностью
21
реентеративный парсер, вариативные аргументы.

2012-02-
5.1.5
17

2011-12-
5,2 Экстренный сборщик мусора, goto, финализаторы для столов.
16

2015-03-
5.2.4
07

Базовая поддержка UTF-8, побитовые операции, 32/64-битные 2015-01-


5,3
целые числа. 12

2017-01-
5.3.4 Последняя версия.
12

https://riptutorial.com/ru/home 3
Examples

Монтаж

Бинарные

Бинарные файлы Lua предоставляются большинством дистрибутивов GNU / Linux в


качестве пакета.

Например, на Debian, Ubuntu и их производных можно получить, выполнив следующее:

sudo apt-get install lua50

sudo apt-get install lua51

sudo apt-get install lua52

В SourceForge есть некоторые полуофициальные сборки для Windows, MacOS и некоторых


других операционных систем.

Пользователи Apple также могут легко установить Lua с помощью Homebrew :

brew install lua

(В настоящее время Homebrew имеет 5.2.4, для 5.3 см. Homebrew / версии .)

Источник

Источник доступен на официальной странице . Приобретение источников и их создание


должны быть тривиальными. В системах Linux должно быть достаточно:

$ wget http://lua.org/ftp/lua-5.3.3.tar.gz
$ echo "a0341bc3d1415b814cc738b2ec01ae56045d64ef ./lua-5.3.3.tar.gz" | sha1sum -c -
$ tar -xvf ./lua-5.3.3.tar.gz
$ make -C ./lua-5.3.3/ linux

В приведенном выше примере мы в основном загружаем исходный tarball с официального


сайта, проверяем его контрольную сумму и извлекаем и выполняем make . (Дважды
проверьте контрольную сумму на официальной странице .)

Примечание: вы должны указать, какую цель сборки вы хотите. В этом примере мы указали
linux . Другие доступные цели сборки включают solaris , aix , bsd , freebsd , macosx , mingw и т.
Д. Для получения более подробной информации macosx doc/readme.html , который включен в
источник. (Вы также можете найти последнюю версию README в Интернете .)

Модули

https://riptutorial.com/ru/home 4
Стандартные библиотеки ограничены примитивами:

• Функции управления сопрограммой coroutine - coroutine


• debug - отладочные крючки и инструменты
• io - основные примитивы ввода-вывода
• функциональность управления package модулей
• string - строка и функциональная совместимость с Lua
• table - примитивы для обработки существенного, но сложного типа Lua - таблицы
• os - основные операции ОС
• utf8 - базовые примитивы UTF-8 (начиная с Lua 5.3)

Все эти библиотеки могут быть отключены для конкретной сборки или загружены во время
выполнения.

Сторонние библиотеки Lua и инфраструктура для распространения модулей редки, но


улучшаются. Такие проекты, как LuaRocks , Lua Toolbox и LuaDist , улучшают ситуацию.
Много информации и множество предложений можно найти на старшей Lua Wiki , но
имейте в виду, что некоторые из этих данных довольно старые и устаревшие.

Комментарии

Однострочные комментарии в Lua начинаются с -- и продолжаются до конца строки:

-- this is single line comment


-- need another line
-- huh?

Заблокировать комментарии начинаются с --[[ и заканчиваются с ]] :

--[[
This is block comment.
So, it can go on...
and on...
and on....
]]

Блочные комментарии используют тот же стиль разделителей, что и длинные строки;


любое количество знаков равенства может быть добавлено между скобками, чтобы
разграничить комментарий:

--[=[
This is also a block comment
We can include "]]" inside this comment
--]=]

--[==[
This is also a block comment
We can include "]=]" inside this comment
--]==]

https://riptutorial.com/ru/home 5
Чистый трюк, чтобы прокомментировать фрагменты кода, состоит в том, чтобы окружить
его --[[ и --]] :

--[[
print'Lua is lovely'
--]]

Чтобы повторно активировать кусок, просто добавьте a - в последовательность открытия


комментария:

---[[
print'Lua is lovely'
--]]

Таким образом, последовательность -- в первой строке запускает однострочный


комментарий, как и предыдущая строка, и оператор print не закомментирован.

Сделав еще один шаг, два блока кода могут быть настроены таким образом, что если
первый блок будет закомментирован, второй не будет, и наоборот:

---[[
print 'Lua is love'
--[=[]]
print 'Lua is life'
--]=]

Чтобы активировать второй фрагмент при отключении первого фрагмента, удалите


ведущий - в первой строке:

--[[
print 'Lua is love'
--[=[]]
print 'Lua is life'
--]=]

Выполнение программ Lua

Обычно Lua поставляется с двумя двоичными файлами:

• lua - автономный интерпретатор и интерактивная оболочка


• компилятор luac - байт-код

Допустим, у нас есть пример программы ( bottles_of_mate.lua ) следующим образом:

local string = require "string"

function bottle_take(bottles_available)

local count_str = "%d bottles of mate on the wall."


local take_str = "Take one down, pass it around, " .. count_str

https://riptutorial.com/ru/home 6
local end_str = "Oh noes, " .. count_str
local buy_str = "Get some from the store, " .. count_str
local bottles_left = 0

if bottles_available > 0 then


print(string.format(count_str, bottles_available))
bottles_left = bottles_available - 1
print(string.format(take_str, bottles_left))
else
print(string.format(end_str, bottles_available))
bottles_left = 99
print(string.format(buy_str, bottles_left))
end

return bottles_left
end

local bottle_count = 99

while true do
bottle_count = bottle_take(bottle_count)
end

Сама программа может быть запущена, выполнив следующую команду в своей оболочке:

$ lua bottles_of_mate.lua

Результат должен выглядеть следующим образом: бег в бесконечном цикле:

Get some from the store, 99 bottles of mate on the wall.


99 bottles of mate on the wall.
Take one down, pass it around, 98 bottles of mate on the wall.
98 bottles of mate on the wall.
Take one down, pass it around, 97 bottles of mate on the wall.
97 bottles of mate on the wall.
...
...
3 bottles of mate on the wall.
Take one down, pass it around, 2 bottles of mate on the wall.
2 bottles of mate on the wall.
Take one down, pass it around, 1 bottles of mate on the wall.
1 bottles of mate on the wall.
Take one down, pass it around, 0 bottles of mate on the wall.
Oh noes, 0 bottles of mate on the wall.
Get some from the store, 99 bottles of mate on the wall.
99 bottles of mate on the wall.
Take one down, pass it around, 98 bottles of mate on the wall.
...

Вы можете скомпилировать программу в байт-код Lua, выполнив следующую команду в


своей оболочке:

$ luac bottles_of_mate.lua -o bottles_of_mate.luac

Также перечисление байт-кода доступно, выполнив следующие действия:

https://riptutorial.com/ru/home 7
$ luac -l bottles_of_mate.lua

main <./bottles.lua:0,0> (13 instructions, 52 bytes at 0x101d530)


0+ params, 4 slots, 0 upvalues, 2 locals, 4 constants, 1 function
1 [1] GETGLOBAL 0 -1 ; require
2 [1] LOADK 1 -2 ; "string"
3 [1] CALL 0 2 2
4 [22] CLOSURE 1 0 ; 0x101d710
5 [22] MOVE 0 0
6 [3] SETGLOBAL 1 -3 ; bottle_take
7 [24] LOADK 1 -4 ; 99
8 [27] GETGLOBAL 2 -3 ; bottle_take
9 [27] MOVE 3 1
10 [27] CALL 2 2 2
11 [27] MOVE 1 2
12 [27] JMP -5 ; to 8
13 [28] RETURN 0 1

function <./bottles.lua:3,22> (46 instructions, 184 bytes at 0x101d710)


1 param, 10 slots, 1 upvalue, 6 locals, 9 constants, 0 functions
1 [5] LOADK 1 -1 ; "%d bottles of mate on the wall."
2 [6] LOADK 2 -2 ; "Take one down, pass it around, "
3 [6] MOVE 3 1
4 [6] CONCAT 2 2 3
5 [7] LOADK 3 -3 ; "Oh noes, "
6 [7] MOVE 4 1
7 [7] CONCAT 3 3 4
8 [8] LOADK 4 -4 ; "Get some from the store, "
9 [8] MOVE 5 1
10 [8] CONCAT 4 4 5
11 [9] LOADK 5 -5 ; 0
12 [11] EQ 1 0 -5 ; - 0
13 [11] JMP 16 ; to 30
14 [12] GETGLOBAL 6 -6 ; print
15 [12] GETUPVAL 7 0 ; string
16 [12] GETTABLE 7 7 -7 ; "format"
17 [12] MOVE 8 1
18 [12] MOVE 9 0
19 [12] CALL 7 3 0
20 [12] CALL 6 0 1
21 [13] SUB 5 0 -8 ; - 1
22 [14] GETGLOBAL 6 -6 ; print
23 [14] GETUPVAL 7 0 ; string
24 [14] GETTABLE 7 7 -7 ; "format"
25 [14] MOVE 8 2
26 [14] MOVE 9 5
27 [14] CALL 7 3 0
28 [14] CALL 6 0 1
29 [14] JMP 15 ; to 45
30 [16] GETGLOBAL 6 -6 ; print
31 [16] GETUPVAL 7 0 ; string
32 [16] GETTABLE 7 7 -7 ; "format"
33 [16] MOVE 8 3
34 [16] MOVE 9 0
35 [16] CALL 7 3 0
36 [16] CALL 6 0 1
37 [17] LOADK 5 -9 ; 99
38 [18] GETGLOBAL 6 -6 ; print
39 [18] GETUPVAL 7 0 ; string
40 [18] GETTABLE 7 7 -7 ; "format"

https://riptutorial.com/ru/home 8
41 [18] MOVE 8 4
42 [18] MOVE 9 5
43 [18] CALL 7 3 0
44 [18] CALL 6 0 1
45 [21] RETURN 5 2
46 [22] RETURN 0 1

Начиная

переменные

var = 50 -- a global variable


print(var) --> 50
do
local var = 100 -- a local variable
print(var) --> 100
end
print(var) --> 50
-- The global var (50) still exists
-- The local var (100) has gone out of scope and can't be accessed any longer.

типы

num = 20 -- a number
num = 20.001 -- still a number
str = "zaldrizes buzdari iksos daor" -- a string
tab = {1, 2, 3} -- a table (these have their own category)
bool = true -- a boolean value
bool = false -- the only other boolean value
print(type(num)) --> 'number'
print(type(str)) --> 'string'
print(type(bool)) --> 'boolean'
type(type(num)) --> 'string'

-- Functions are a type too, and first-class values in Lua.


print(type(print)) --> prints 'function'
old_print = print
print = function (x) old_print "I'm ignoring the param you passed me!" end
old_print(type(print)) --> Still prints 'function' since it's still a function.
-- But we've (unhelpfully) redefined the behavior of print.
print("Hello, world!") --> prints "I'm ignoring the param you passed me!"

Специальный тип nil


Другой тип в Lua - nil . Единственное значение в nil типа является nil . nil существует,
чтобы отличаться от всех других значений в Lua. Это своего рода неценовое значение.

print(foo) -- This prints nil since there's nothing stored in the variable 'foo'.
foo = 20
print(foo) -- Now this prints 20 since we've assigned 'foo' a value of 20.

-- We can also use `nil` to undefine a variable

https://riptutorial.com/ru/home 9
foo = nil -- Here we set 'foo' to nil so that it can be garbage-collected.

if nil then print "nil" end --> (prints nothing)


-- Only false and nil are considered false; every other value is true.
if 0 then print "0" end --> 0
if "" then print "Empty string!" --> Empty string!

выражения

a = 3
b = a + 20 a = 2 print(b, a) -- hard to read, can also be written as
b = a + 20; a = 2; print(a, b) -- easier to read, ; are optional though
true and true --> returns true
true and 20 --> 20
false and 20 --> false
false or 20 --> 20
true or 20 --> true
tab or {}
--> returns tab if it is defined
--> returns {} if tab is undefined
-- This is useful when we don't know if a variable exists
tab = tab or {} -- tab stays unchanged if it exists; tab becomes {} if it was previously nil.

a, b = 20, 30 -- this also works


a, b = b, a -- switches values

Определение функций

function name(parameter)
return parameter
end
print(name(20)) --> 20
-- see function category for more information
name = function(parameter) return parameter end -- Same as above

булевы
Только false и nil оцениваются как false, все остальное, включая 0 а пустая строка
оценивается как истина.

вывоз мусора

tab = {"lots", "of", "data"}


tab = nil; collectgarbage()
-- tab does no longer exist, and doesn't take up memory anymore.

таблицы

https://riptutorial.com/ru/home 10
tab1 = {"a", "b", "c"}
tab2 = tab1
tab2[1] = "d"
print(tab1[1]) --> 'd' -- table values only store references.
--> assigning tables does not copy its content, only the reference.

tab2 = nil; collectgarbage()


print(tab1) --> (prints table address) -- tab1 still exists; it didn't get garbage-collected.

tab1 = nil; collectgarbage()


-- No more references. Now it should actually be gone from memory.

Это основы, но есть раздел о таблицах с дополнительной информацией.

условия

if (condition) then
-- do something
elseif (other_condition) then
-- do something else
else
-- do something
end

для петель
Существует два типа цикла for в Lua: числовое значение for цикла и общий for цикла.

• Цифры for цикла имеют следующий вид:

for a=1, 10, 2 do -- for a starting at 1, ending at 10, in steps of 2


print(a) --> 1, 3, 5, 7, 9
end

Третье выражение в числовой for цикла представляет собой этап , по которому цикл
будет увеличиваться. Это упрощает выполнение обратных циклов:

for a=10, 1, -1 do
print(a) --> 10, 9, 8, 7, 6, etc.
end

Если выражение шага не указано, Lua принимает шаг по умолчанию 1.

for a=1, 10 do
print(a) --> 1, 2, 3, 4, 5, etc.
end

Также обратите внимание, что переменная цикла является локальной for цикла for .
Он не будет существовать после завершения цикла.

https://riptutorial.com/ru/home 11
• Общие for циклов работают через все значения, возвращаемые функцией итератора:

for key, value in pairs({"some", "table"}) do


print(key, value)
--> 1 some
--> 2 table
end

Lua предоставляет несколько встроенных итераторов (например, pairs , ipairs ), и


пользователи могут определять свои собственные итераторы, а также использовать
общие for циклов.

делать блоки

local a = 10
do
print(a) --> 10
local a = 20
print(a) --> 20
end
print(a) --> 10

Некоторые сложные вещи

Иногда Lua не ведет себя так, как можно было бы подумать после прочтения
документации. Некоторые из этих случаев:

Ниль и Ничто не то же самое (ОБЩИЙ


ПИТФАЛ!)
Как и ожидалось, table.insert(my_table, 20) добавляет значение 20 в таблицу, а
table.insert(my_table, 5, 20) добавляет значение 20 в 5-ю позицию. Что делает
table.insert(my_table, 5, nil) ? Можно было бы ожидать, что он будет обрабатывать nil как
никакой аргумент вообще и вставить значение 5 в конце таблицы, но фактически
добавляет значение nil в пятую позицию таблицы. Когда это проблема?

(function(tab, value, position)


table.insert(tab, position or value, position and value)
end)({}, 20)
-- This ends up calling table.insert({}, 20, nil)
-- and this doesn't do what it should (insert 20 at the end)

Аналогичная ситуация происходит с tostring() :

print (tostring(nil)) -- this prints "nil"


table.insert({}, 20) -- this returns nothing

https://riptutorial.com/ru/home 12
-- (not nil, but actually nothing (yes, I know, in lua those two SHOULD
-- be the same thing, but they aren't))

-- wrong:
print (tostring( table.insert({}, 20) ))
-- throws error because nothing ~= nil

--right:
local _tmp = table.insert({}, 20) -- after this _tmp contains nil
print(tostring(_tmp)) -- prints "nil" because suddenly nothing == nil

Это также может привести к ошибкам при использовании стороннего кода. Если,
например, документация некоторых состояний функций «возвращает пончики, если
повезет, нуль в противном случае», реализация может выглядеть примерно так:

function func(lucky)
if lucky then
return "donuts"
end
end

эта реализация может показаться разумной вначале; он возвращает пончики, когда это
необходимо, и когда вы вводите result = func(false) результат будет содержать значение
nil .

Тем не менее, если нужно написать print(tostring(func(false))) lua выдаст ошибку, которая
выглядит примерно так stdin:1: bad argument #1 to 'tostring' (value expected)

Это почему? tostring явно получает аргумент, хотя его nil . Неправильно. func ничего не
возвращает, поэтому tostring(func(false)) является тем же, что и tostring() и NOT то же,
что и tostring(nil) .

Ошибки, говорящие о «ожидаемом значении», являются убедительным доказательством


того, что это может быть источником проблемы.

Устранение пробелов в массивах


Это огромная ошибка, если вы новичок в lua, и в таблице есть много информации

Привет, мир

Это приветственный мировой код:

print("Hello World!")

Как это устроено? Это просто! Lua выполняет функцию print() и использует строку "Hello
World" качестве аргумента.

https://riptutorial.com/ru/home 13
Прочитайте Начало работы с Lua онлайн: https://riptutorial.com/ru/lua/topic/659/начало-
работы-с-lua

https://riptutorial.com/ru/home 14
глава 2: Pico-8
Вступление
PICO-8 - это фэнтезийная консоль, запрограммированная во встроенной Lua. У него уже
есть хорошая документация . Используйте этот раздел, чтобы продемонстрировать
недокументированные или недостаточно документированные функции.

Examples

Игровой цикл

Вполне возможно использовать PICO-8 в качестве интерактивной оболочки , но вы,


вероятно, захотите задействовать игровой цикл. Чтобы сделать это, вы должны создать
хотя бы одну из этих функций обратного вызова:

• _update()

• _update60() (после v0.1.8 )


• _draw()

Минимальная «игра» может просто нарисовать что-то на экране:

function _draw()
cls()
print("a winner is you")
end

Если вы определяете _update60() , игровой цикл пытается работать со скоростью 60 кадров


в секунду и игнорирует update() (который работает со скоростью 30 кадров в секунду).
Любая функция update вызывается перед _draw() . Если система обнаруживает выпавшие
кадры, она пропускает функцию рисования каждый другой кадр, поэтому лучше всего
держать логику игры и проигрывателя в функции обновления:

function _init()
x = 63
y = 63

cls()
end

function _update()
local dx = 0 dy = 0

if (btn(0)) dx-=1
if (btn(1)) dx+=1
if (btn(2)) dy-=1
if (btn(3)) dy+=1

https://riptutorial.com/ru/home 15
x+=dx
y+=dy
x%=128
y%=128
end

function _draw()
pset(x,y)
end

Функция _init() , строго говоря, необязательна, поскольку команды за пределами любой


функции запускаются при запуске. Но это удобный способ сбросить игру до начальных
условий без перезагрузки картриджа:

if (btn(4)) _init()

Вход для мыши

Хотя это официально не поддерживается, вы можете использовать ввод мыши в своих


играх:

function _update60()
x = stat(32)
y = stat(33)

if (x>0 and x<=128 and


y>0 and y<=128)
then

-- left button
if (band(stat(34),1)==1) then
ball_x=x
ball_y=y
end
end

-- right button
if (band(stat(34),2)==2) then
ball_c+=1
ball_c%=16
end

-- middle button
if (band(stat(34),4)==4) then
ball_r+=1
ball_r%=64
end
end

function _init()
ball_x=63
ball_y=63
ball_c=10
ball_r=1
end

https://riptutorial.com/ru/home 16
function _draw()
cls()
print(stat(34),1,1)
circ(ball_x,ball_y,ball_r,ball_c)
pset(x,y,7) -- white
end

Режимы игры

Если вам нужен экран заголовка или экран эндшпиля, рассмотрите настройку механизма
переключения режима:

function _init()
mode = 1
end

function _update()
if (mode == 1) then
if (btnp(5)) mode = 2
elseif (mode == 2) then
if (btnp(5)) mode = 3
end
end

function _draw()
cls()
if (mode == 1) then
title()
elseif (mode == 2) then
print("press 'x' to win")
else
end_screen()
end
end

function title()
print("press 'x' to start game")
end

function end_screen()
print("a winner is you")
end

Прочитайте Pico-8 онлайн: https://riptutorial.com/ru/lua/topic/8715/pico-8

https://riptutorial.com/ru/home 17
глава 3: Булевы в Lua
замечания
Булевы, истина и ложность прямолинейны в Lua. Для обзора:

1. Существует логический тип с двумя значениями: true и false .


2. В условном контексте ( if , elseif , while , until ) логическое значение не требуется.
Можно использовать любое выражение.
3. В условном контексте false и nil считаются ложными, а все остальное считается
истинным.
4. Хотя 3 уже подразумевает это: если вы исходите из других языков, помните, что 0 а
пустая строка считается как true в условных контекстах в Lua.

Examples

Булевский тип

Булевы и другие значения


При работе с lua важно различать логические значения true и false и значения, которые
оцениваются как true или false.

В lua есть только два значения, которые вычисляют false: nil и false , тогда как все
остальное, включая числовое значение 0 оценивается как true.

Некоторые примеры того, что это означает:

if 0 then print("0 is true") end --> this will print "true"


if (2 == 3) then print("true") else print("false") end --> this prints "false"
if (2 == 3) == false then print("true") end --> this prints "true"
if (2 == 3) == nil then else print("false") end
--> prints false, because even if nil and false both evaluate to false,
--> they are still different things.

Логические операции
Логические операторы в lua необязательно возвращают логические значения:

and вернет второе значение, если первое значение будет равно true;

or возвращает второе значение, если первое значение имеет значение false;

https://riptutorial.com/ru/home 18
Это позволяет моделировать тернарный оператор, как и на других языках:

local var = false and 20 or 30 --> returns 30


local var = true and 20 or 30 --> returns 20
-- in C: false ? 20 : 30

Это также можно использовать для инициализации таблиц, если они не существуют

tab = tab or {} -- if tab already exists, nothing happens

или избегать использования операторов if, делая код более удобным для чтения

print(debug and "there has been an error") -- prints "false" line if debug is false
debug and print("there has been an error") -- does nothing if debug is false
-- as you can see, the second way is preferable, because it does not output
-- anything if the condition is not met, but it is still possible.
-- also, note that the second expression returns false if debug is false,
-- and whatever print() returns if debug is true (in this case, print returns nil)

Проверка наличия переменных


Можно также легко проверить, существует ли переменная (если она определена),
поскольку несуществующие переменные возвращают значение nil , которое вычисляется
как false.

local tab_1, tab_2 = {}


if tab_1 then print("table 1 exists") end --> prints "table 1 exists"
if tab_2 then print("table 2 exists") end --> prints nothing

Единственный случай, когда это не применяется, - это когда переменная хранит значение
false , и в этом случае оно технически существует, но все равно оценивается как false. Из-
за этого плохой дизайн создает функции, которые возвращают false и nil зависимости от
состояния или ввода. Мы все еще можем проверить, есть ли у нас nil или false :

if nil == nil then print("A nil is present") else print("A nil is not present") end
if false == nil then print("A nil is present") else print("A nil is not present") end
-- The output of these calls are:
-- A nil is present!
-- A nil is not present

Условные контексты

Условные контексты в Lua ( if , elseif , while , until ) не требуют булевых. Как и многие
языки, любое значение Lua может появляться в состоянии. Правила для оценки просты:

1. false и nil считаются ложными.

https://riptutorial.com/ru/home 19
2. Все остальное считается истинным.

if 1 then
print("Numbers work.")
end
if 0 then
print("Even 0 is true")
end

if "strings work" then


print("Strings work.")
end
if "" then
print("Even the empty string is true.")
end

Логические операторы

В Lua логические операции можно манипулировать с помощью логических операторов . К


этим операторам относятся not , and , и / or .

В простых выражениях результаты довольно просты:

print(not true) --> false


print(not false) --> true
print(true or false) --> true
print(false and true) --> false

Приказ о приоритете
Порядок приоритета подобен математическим операторам унарный - , * и + :

• not

• затем and
• затем or

Это может привести к сложным выражениям:

print(true and false or not false and not true)


print( (true and false) or ((not false) and (not true)) )
--> these are equivalent, and both evaluate to false

Краткосрочная оценка
Операторы and or могут быть оценены только с помощью первого операнда, если вторая не
нужна:

https://riptutorial.com/ru/home 20
function a()
print("a() was called")
return true
end

function b()
print("b() was called")
return false
end

print(a() or b())
--> a() was called
--> true
-- nothing else
print(b() and a())
--> b() was called
--> false
-- nothing else
print(a() and b())
--> a() was called
--> b() was called
--> false

Идиоматический условный оператор


Из - за приоритет логических операторов, возможность для оценки короткой резки и
оценки , не являющихся false и не- nil значений , как true , идиоматический условный
оператор доступны в Lua:

function a()
print("a() was called")
return false
end
function b()
print("b() was called")
return true
end
function c()
print("c() was called")
return 7
end

print(a() and b() or c())


--> a() was called
--> c() was called
--> 7

print(b() and c() or a())


--> b() was called
--> c() was called
--> 7

Кроме того , в связи с характером x and a or b структуры, никогда не будет возвращен ,


если она принимает значение a false , это условное будет всегда возвращать b ,
независимо от того , что x есть.

https://riptutorial.com/ru/home 21
print(true and false or 1) -- outputs 1

Таблицы правды

Логические операторы в Lua не возвращают логическое значение, а один из аргументов.


Используя nil для false и числа для true, вот как они себя ведут.

print(nil and nil) -- nil


print(nil and 2) -- nil
print(1 and nil) -- nil
print(1 and 2) -- 2

print(nil or nil) -- nil


print(nil or 2) -- 2
print(1 or nil) -- 1
print(1 or 2) -- 1

Как вы можете видеть, Lua всегда будет возвращать первое значение, которое делает
проверку неудачной или успешной . Вот таблицы правды, показывающие это.

x | y || and x | y || or
------------------ ------------------
false|false|| x false|false|| y
false|true || x false|true || y
true |false|| y true |false|| x
true |true || y true |true || x

Для тех, кто в ней нуждается, вот две функции, представляющие эти логические
операторы.

function exampleAnd(value1, value2)


if value1 then
return value2
end
return value1
end

function exampleOr(value1, value2)


if value1 then
return value1
end
return value2
end

Эмулирующий тернарный оператор с «и» или «логическими


операторами».

В lua логические операторы and or возвращают один из операндов в качестве результата


вместо логического результата. Как следствие, этот механизм можно использовать для
подражания поведению тройного оператора, несмотря на то, что он не имеет «реального»
тернарного оператора в языке.

https://riptutorial.com/ru/home 22
Синтаксис
условие и правша_expr или falsey_expr

Использование в переменной
присваивании / инициализации
local drink = (fruit == "apple") and "apple juice" or "water"

Использование в конструкторе таблицы


local menu =
{
meal = vegan and "carrot" or "steak",
drink = vegan and "tea" or "chicken soup"
}

Использовать в качестве аргумента


функции
print(age > 18 and "beer" or "fruit punch")

Использовать в возвратной инструкции


function get_gradestring(student)
return student.grade > 60 and "pass" or "fail"
end

Предостережение
Бывают ситуации, когда этот механизм не имеет желаемого поведения. Рассмотрим этот
случай

local var = true and false or "should not happen"

https://riptutorial.com/ru/home 23
В «реальном» тернарном операторе ожидаемое значение var является false . В Lua,
однако, and оценка «проваливается» , потому что второй операнд falsey. В результате var
заканчивается, а should not happen вместо этого.

Два возможных решения этой проблемы реорганизуют это выражение, поэтому средний
операнд не является ложным. например.

local var = not true and "should not happen" or false

или, альтернативно, использовать классическую, if then else построить.

Прочитайте Булевы в Lua онлайн: https://riptutorial.com/ru/lua/topic/3101/булевы-в-lua

https://riptutorial.com/ru/home 24
глава 4: Вариадические аргументы
Вступление
Varargs , как они обычно известны, позволяют выполнять произвольное количество
аргументов без спецификации. Все аргументы, заданные такой функции, упаковываются в
единую структуру, известную как список vararg ; который написан как ... в Луа.
Существуют основные методы для извлечения числа заданных аргументов и значения этих
аргументов с помощью функции select() , но более продвинутые шаблоны использования
могут использовать структуру для полной утилиты.

Синтаксис
• ... - Делает функцию, список аргументов которой, в которой появляется переменная
функция
• select (what, ...) - Если «what» - это число в диапазоне 1 к числу элементов в vararg,
возвращает элемент «what'th» последнему элементу в vararg. Возврат будет равен
нулю, если индекс не соответствует границам. Если «что» является строкой «#»,
возвращается количество элементов в vararg.

замечания
КПД

Список vararg реализуется как связанный список в реализации языка PUC-Rio, это
означает, что индексы O (n). Это означает, что итерация по элементам в vararg с помощью
select() , как и в примере ниже, является операцией O (n ^ 2).

for i = 1, select('#', ...) do


print(select(i, ...))
end

Если вы планируете повторять элементы в списке vararg, сначала упакуйте список в


таблицу. Доступ к таблице - это O (1), поэтому повторение выполняется полностью O (n).
Или, если вы так склонны, см. Пример foldr() из расширенного раздела использования; он
использует рекурсию для итерации по списку vararg в O (n).

Определение длины последовательности

Vararg полезен тем, что длина vararg соответствует любым явно переданным (или
вычисленным) нулям. Например.

https://riptutorial.com/ru/home 25
function test(...)
return select('#', ...)
end

test() --> 0
test(nil, 1, nil) --> 3

Однако это поведение противоречит поведению таблиц, когда оператор длины # не


работает с «дырками» (внедренными нилями) в последовательности. Вычисление длины
таблицы с отверстиями не определено и на него нельзя положиться. Таким образом, в
зависимости от значений в ... , длина {...} не может привести к «правильному» ответу. В
Lua 5.2+ для управления этим недостатком был введен table.pack() (в примере реализована
функция, реализующая эту функцию в чистом Lua).

Идиоматическое использование

Поскольку varargs несут свою длину, люди используют их как последовательности, чтобы
избежать проблем с отверстиями в таблицах. Это не было их предполагаемое
использование, и одна эталонная реализация Lua не оптимизирована. Хотя такое
использование рассматривается в примерах, оно, как правило, неодобрительно.

Examples

основы

Функции Variadic создаются с использованием синтаксиса ... эллипсов в списке


аргументов определения функции.

function id(...)
return
end

Если вы назвали эту функцию id(1, 2, 3, 4, 5) тогда ... (AKA - список vararg) будет
содержать значения 1, 2, 3, 4, 5 .

Функции могут принимать требуемые аргументы, а также ...

function head(x, ...)


return x
end

Самый простой способ вытащить элементы из списка vararg - просто назначить


переменные из него.

function head3(...)
local a, b, c = ...
return a, b, c
end

https://riptutorial.com/ru/home 26
также может использоваться для поиска количества элементов и извлечения
select()
элементов из ... косвенно.

function my_print(...)
for i = 1, select('#', ...) do
io.write(tostring(select(i, ...)) .. '\t')
end
io.write '\n'
end

...могут быть упакованы в таблицу для удобства использования, используя {...} . Это
помещает все аргументы в последовательную часть таблицы.

5,2

table.pack(...) также можно использовать для упаковки списка vararg в таблицу.


Преимущество table.pack(...) состоит в том, что он устанавливает поле n возвращенной
таблицы в значение select('#', ...) . Это важно, если список аргументов может содержать
nils (см. Раздел ниже).

function my_tablepack(...)
local t = {...}
t.n = select('#', ...)
return t
end

Список vararg также может быть возвращен из функций. Результат - несколько возвратов.

function all_or_none(...)
local t = table.pack(...)
for i = 1, t.n do
if not t[i] then
return -- return none
end
end
return ... -- return all
end

Расширенное использование

Как указано в основных примерах, вы можете иметь переменные связанные переменные и


список аргументов переменных ( ... ). Вы можете использовать этот факт, чтобы
рекурсивно разделить список, как на других языках (например, Haskell). Ниже приведена
реализация foldr() которая использует это. Каждый рекурсивный вызов связывает головку
списка vararg с x и передает остальную часть списка рекурсивному вызову. Это разрушает
список, пока не будет только один аргумент ( select('#', ...) == 0 ). После этого каждое
значение применяется к аргументу функции f с ранее вычисленным результатом.

function foldr(f, ...)


if select('#', ...) < 2 then return ... end

https://riptutorial.com/ru/home 27
local function helper(x, ...)
if select('#', ...) == 0 then
return x
end
return f(x, helper(...))
end
return helper(...)
end

function sum(a, b)
return a + b
end

foldr(sum, 1, 2, 3, 4)
--> 10

Вы можете найти другие определения функций, которые используют этот стиль


программирования здесь в выпуске № 3 по номеру 8.

Единственная идиоматическая структура данных Lua - это таблица. Оператор длины


таблицы не определен, если nil s находится где угодно в последовательности. В отличие
от таблиц, список vararg учитывает явные значения nil s, как указано в базовых примерах и
в разделе замечаний (пожалуйста, прочитайте этот раздел, если вы еще этого не сделали).
При небольшой работе список vararg может выполнять каждую операцию, кроме таблицы,
помимо мутации. Это делает список vararg хорошим кандидатом для реализации
неизменяемых кортежей.

function tuple(...)
-- packages a vararg list into an easily passable value
local co = coroutine.wrap(function(...)
coroutine.yield()
while true do
coroutine.yield(...)
end
end)
co(...)
return co
end

local t = tuple((function() return 1, 2, nil, 4, 5 end)())

print(t()) --> 1 2 nil 4 5 | easily unpack for multiple args


local a, b, d = t() --> a = 1, b = 2, c = nil | destructure the tuple
print((select(4, t()))) --> 4 | index the tuple
print(select('#', t())) --> 5 | find the tuple arity (nil
respecting)

local function change_index(tpl, i, v)


-- sets a value at an index in a tuple (non-mutating)
local function helper(n, x, ...)
if select('#', ...) == 0 then
if n == i then
return v
else
return x
end

https://riptutorial.com/ru/home 28
else
if n == i then
return v, helper(n+1, ...)
else
return x, helper(n+1, ...)
end
end
end
return tuple(helper(1, tpl()))
end

local n = change_index(t, 3, 3)
print(t()) --> 1 2 nil 4 5
print(n()) --> 1 2 3 4 5

Основное различие между тем, что выше и таблицами, состоит в том, что таблицы
изменяемы и имеют семантику указателя, где кортеж не имеет этих свойств. Кроме того,
кортежи могут содержать явные nil s и иметь операцию с бесконечной длиной.

Прочитайте Вариадические аргументы онлайн: https://riptutorial.com/ru/lua/topic/4475/


вариадические-аргументы

https://riptutorial.com/ru/home 29
глава 5: Введение в API Lua C
Синтаксис
• lua_State * L = lua_open (); // Создаем новое состояние виртуальной машины; Lua 5.0
• lua_State * L = luaL_newstate (); // Создаем новое состояние виртуальной машины; Lua
5.1+
• int luaL_dofile (lua_State * L, const char * имя_файла ); // Запускаем сценарий lua с
заданным именем файла с использованием указанного lua_State
• void luaL_openlibs (lua_State * L); // Загрузите все стандартные библиотеки в
указанный lua_State
• void lua_close (lua_State * L); // Закрываем состояние VM и освобождаем любые
ресурсы внутри
• void lua_call (lua_State * L, int nargs, int nresults); // Вызов luavalue при индексе - (nargs +
1)

замечания
Lua также обеспечивает надлежащий C API для своей виртуальной машины. В отличие от
самой VM, интерфейс API C основан на стеке. Таким образом, большинство функций,
предназначенных для использования с данными, - это либо добавление некоторых вещей
поверх виртуального стека, либо удаление из него. Кроме того, все вызовы API должны
быть тщательно использованы в стеке и его ограничения.

В общем, все, что доступно на языке Lua, может быть выполнено с использованием C API.
Кроме того, есть некоторые дополнительные функции, такие как прямой доступ к
внутреннему реестру, изменение поведения стандартного распределителя памяти или
сборщика мусора.

Вы можете скомпилировать предоставленные примеры API Lua C, выполнив следующие


действия на вашем терминале:

$ gcc -Wall ./example.c -llua -ldl -lm

Examples

Создание виртуальной машины Lua

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

https://riptutorial.com/ru/home 30
int main(void)
{

5,1

/* Start by creating a new VM state */


lua_State *L = luaL_newstate();

/* Load standard Lua libraries: */


luaL_openlibs(L);

5,1

/* For older version of Lua use lua_open instead */


lua_State *L = lua_open();

/* Load standard libraries*/


luaopen_base(L);
luaopen_io(L);
luaopen_math(L);
luaopen_string(L);
luaopen_table(L);

/* do stuff with Lua VM. In this case just load and execute a file: */
luaL_dofile(L, "some_input_file.lua");

/* done? Close it then and exit. */


lua_close(L);

return EXIT_SUCCESS;
}

Вызов функций Lua

#include <stdlib.h>

#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>

int main(void)
{
lua_State *lvm_hnd = lua_open();
luaL_openlibs(lvm_hnd);

/* Load a standard Lua function from global table: */


lua_getglobal(lvm_hnd, "print");

/* Push an argument onto Lua C API stack: */


lua_pushstring(lvm_hnd, "Hello C API!");

/* Call Lua function with 1 argument and 0 results: */


lua_call(lvm_hnd, 1, 0);

lua_close(lvm_hnd);

return EXIT_SUCCESS;

https://riptutorial.com/ru/home 31
}

В приведенном выше примере мы делаем следующее:

• создание и настройка Lua VM, как показано на первом примере


• получение и нажатие функции Lua из глобальной таблицы Lua на виртуальный стек
• нажатие строки "Hello C API" в качестве входного аргумента в виртуальный стек
• указание виртуальной машины на вызов функции с одним аргументом, который уже
находится в стеке
• закрытие и очистка

НОТА:

lua_call() , что lua_call() функцию, и ее аргументы из стека оставляют только результат.

Кроме того, было бы безопаснее использовать Lua protected call - lua_pcall() .

Встроенный интерпретатор Lua с пользовательским API и настройкой Lua

Продемонстрируйте, как встраивать интерпретатор lua в код C, выставлять C-


определенную функцию в сценарий Lua, оценивать сценарий Lua, вызывать C-
определенную функцию из Lua и вызывать Lua-определенную функцию из C (хоста).

В этом примере мы хотим, чтобы настроение было настроено с помощью сценария Lua. Вот
mood.lua:

-- Get version information from host


major, minor, build = hostgetversion()
print( "The host version is ", major, minor, build)
print("The Lua interpreter version is ", _VERSION)

-- Define a function for host to call


function mood( b )

-- return a mood conditional on parameter


if (b and major > 0) then
return 'mood-happy'
elseif (major == 0) then
return 'mood-confused'
else
return 'mood-sad'
end
end

Обратите внимание: mood() не вызывается в скрипте. Он просто определен для вызова


хост-приложения. Также обратите внимание, что скрипт вызывает функцию
hostgetversion() которая не определена в скрипте.

Затем мы определяем хост-приложение, которое использует «mood.lua». Вот «hostlua.c»:

https://riptutorial.com/ru/home 32
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

/*
* define a function that returns version information to lua scripts
*/
static int hostgetversion(lua_State *l)
{
/* Push the return values */
lua_pushnumber(l, 0);
lua_pushnumber(l, 99);
lua_pushnumber(l, 32);

/* Return the count of return values */


return 3;
}

int main (void)


{
lua_State *l = luaL_newstate();
luaL_openlibs(l);

/* register host API for script */


lua_register(l, "hostgetversion", hostgetversion);

/* load script */
luaL_dofile(l, "mood.lua");

/* call mood() provided by script */


lua_getglobal(l, "mood");
lua_pushboolean(l, 1);
lua_call(l, 1, 1);

/* print the mood */


printf("The mood is %s\n", lua_tostring(l, -1));
lua_pop(l, 1);

lua_close(l);
return 0;
}

И вот вывод:

The host version is 0 99 32


Lua interpreter version is Lua 5.2
The mood is mood-confused

Даже после компиляции «hostlua.c» мы по-прежнему можем изменять «mood.lua», чтобы


изменить выход нашей программы!

Обработка таблиц

Чтобы получить доступ или изменить индекс в таблице, вам нужно как-то поместить
таблицу в стек.

https://riptutorial.com/ru/home 33
Предположим, что для этих примеров ваша таблица является глобальной переменной с
именем tbl.

Получение содержимого по определенному индексу:

int getkey_index(lua_State *L)


{
lua_getglobal(L, "tbl"); // this put the table in the stack
lua_pushstring(L, "index"); // push the key to access
lua_gettable(L, -2); // retrieve the corresponding value; eg. tbl["index"]

return 1; // return value to caller


}

Как мы видели, все, что вам нужно сделать, - это нажать таблицу в стек, нажать индекс и
вызвать lua_gettable. аргумент -2 означает, что таблица является вторым элементом из
вершины стека.
lua_gettable запускает метаметоды. Если вы не хотите запускать метаметоды, вместо этого
используйте lua_rawget. Он использует те же аргументы.

Настройка содержимого по определенному индексу:

int setkey_index(lua_State *L)


{
// setup the stack
lua_getglobal(L, "tbl");
lua_pushstring(L, "index");
lua_pushstring(L, "value");
// finally assign the value to table; eg. tbl.index = "value"
lua_settable(L, -3);

return 0;
}

То же упражнение, что и получение контента. Вам нужно нажать на стек, нажать индекс,
а затем нажать значение в стек. после этого вы вызываете lua_settable. аргумент -3 - это
позиция таблицы в стеке. Чтобы избежать запуска метаметодов, используйте lua_rawset
вместо lua_settable. Он использует те же аргументы.

Перенос содержимого из таблицы в другую:

int copy_tableindex(lua_State *L)


{
lua_getglobal(L, "tbl1"); // (tbl1)
lua_getglobal(L, "tbl2");// (tbl1)(tbl2)
lua_pushstring(L, "index1");// (tbl1)(tbl2)("index1")
lua_gettable(L, -3);// (tbl1)(tbl2)(tbl1.index1)
lua_pushstring(L, "index2");// (tbl1)(tbl2)(tbl1.index1)("index2")
lua_pushvalue(L, -2); // (tbl1)(tbl2)(tbl1.index1)("index2")(tbl1.index1)

https://riptutorial.com/ru/home 34
lua_settable(L, -4);// (tbl1)(tbl2)(tbl1.index1)
lua_pop(L, 1);

return 0;
}

Теперь мы собираем все, что мы узнали здесь. Я помещал содержимое стека в


комментарии, чтобы вы не потерялись.

Мы помещаем обе таблицы в стек, вставляем индекс таблицы 1 в стек и получаем значение
в tbl1.index1 . Обратите внимание на аргумент -3 на gettable. Я смотрю на первый стол
(третий сверху), а не на второй. Затем мы нажимаем индекс второй таблицы, копируем
tbl1.index1 в верхнюю часть стека, а затем вызываем lua_settable , на четвертый элемент
сверху.

Для удобства уборки я очистил верхний элемент, так что только две таблицы остаются в
стеке.

Прочитайте Введение в API Lua C онлайн: https://riptutorial.com/ru/lua/topic/671/введение-в-


api-lua-c

https://riptutorial.com/ru/home 35
глава 6: итераторы
Examples

Общий для цикла

Итераторы используют форму цикла for известного как общий для цикла .

Общая форма цикла for использует три параметра:

1. Функция итератора , вызываемая при следующем значении. В качестве параметров


он получает как инвариантное состояние, так и управляющую переменную. Возврат
nil сигнала.
2. Инвариантное состояние - это значение, которое не изменяется во время итерации.
Обычно он является объектом итератора, например таблицы, строки или
пользовательских данных.
3. Управляющая переменная представляет собой начальное значение для итерации.

Мы можем написать цикл for для итерации всех пар ключ-значение в таблице с
использованием следующей функции.

local t = {a=1, b=2, c=3, d=4, e=5}

-- next is the iterator function


-- t is the invariant state
-- nil is the control variable (calling next with a nil gets the first key)
for key, value in next, t, nil do
-- key is the new value for the control variable
print(key, value)
-- Lua calls: next(t, key)
end

Стандартные итераторы

Стандартная библиотека Lua предоставляет две функции итератора, которые могут


использоваться с циклом for чтобы пересекать пары ключ-значение в таблицах.

Для итерации по таблице последовательностей мы можем использовать библиотечную


функцию ipairs .

for index, value in ipairs {'a', 'b', 'c', 'd', 'e'} do


print(index, value) --> 1 a, 2 b, 3 c, 4 d, 5 e
end

Для итератора по всем ключам и значениям в любой таблице мы можем использовать пар
функции библиотеки.

https://riptutorial.com/ru/home 36
for key, value in pairs {a=1, b=2, c=3, d=4, e=5} do
print(key, value) --> e 5, c 3, a 1, b 2, d 4 (order not specified)
end

Итераторы без гражданства

Обе пары и ipairs представляют итераторы без атак . Итератор без сохранения состояния
использует только общие для управляющей переменной цикла и инвариантного состояния
для вычисления значения итерации.

Пары Итератор
Мы можем реализовать итератор pairs апатридов, используя next функцию.

-- generator function which initializes the generic for loop


local function pairs(t)
-- next is the iterator function
-- t is the invariant state
-- control variable is nil
return next, t, nil
end

Итератор Ipairs
Мы можем реализовать ipairs без ipairs в двух отдельных функциях.

-- function which performs the actual iteration


local function ipairs_iter(t, i)
local i = i + 1 -- next index in the sequence (i is the control variable)
local v = t[i] -- next value (t is the invariant state)
if v ~= nil then
return i, v -- index, value
end
return nil -- no more values (termination)
end

-- generator function which initializes the generic for loop


local function ipairs(t)
-- ipairs_iter is the iterator function
-- t is the invariant state (table to be iterated)
-- 0 is the control variable (first index)
return ipairs_iter, t, 0
end

Итератор символов
Мы можем создать новые итераторы без сохранения состояния, выполнив контракт
генератора for цикла.

https://riptutorial.com/ru/home 37
-- function which performs the actual iteration
local function chars_iter(s, i)
if i < #s then
i = i + 1
return i, s:sub(i, i)
end
end

-- generator function which initializes the generic for loop


local function chars(s)
return chars_iter, s, 0
end

-- used like pairs and ipairs


for i, c in chars 'abcde' do
print(i, c) --> 1 a, 2 b, 3 c, 4 f, 5 e
end

Имитатор первичных чисел


Это еще один простой пример итератора без состояния.

-- prime numbers iterator


local incr = {4, 1, 2, 0, 2}
function primes(s, p, d)
s, p, d = s or math.huge, p and p + incr[p % 6] or 2, 1
while p <= s do
repeat
d = d + incr[d % 6]
if d*d > p then return p end
until p % d == 0
p, d = p + incr[p % 6], 1
end
end

-- print all prime numbers <= 100


for p in primes, 100 do -- passing in the iterator (do not call the iterator here)
print(p) --> 2 3 5 7 11 ... 97
end

-- print all primes in endless loop


for p in primes do -- please note: "in primes", not "in primes()"
print(p)
end

Истребители состояния

Истираторы состояния содержат дополнительную информацию о текущем состоянии


итератора.

Использование таблиц
Состояние добавления может быть упаковано в общий для инвариантного состояния цикла
.

https://riptutorial.com/ru/home 38
local function chars_iter(t, i)
local i = i + 1
if i <= t.len then
return i, t.s:sub(i, i)
end
end

local function chars(s)


-- the iterators state
local t = {
s = s, -- the subject
len = #s -- cached length
}
return chars_iter, t, 0
end

for i, c in chars 'abcde' do


print(i, c) --> 1 a, 2 b, 3 c, 4 d, 5 e
end

Использование закрытий
Дополнительное состояние можно обернуть в пределах закрытия функции. Поскольку
состояние полностью содержится в объеме замыкания, инвариантное состояние и
управляющая переменная не нужны.

local function chars(s)


local i, len = 0, #s
return function() -- iterator function
i = i + 1
if i <= len then
return i, s:sub(i, i)
end
end
end

for i, c in chars 'abcde' do


print(i, c) --> 1 a, 2 b, 3 c, 4 d, 5 e
end

Использование Coroutines
Дополнительное состояние может содержаться внутри сопрограммы, снова не требуется
инвариантное состояние и управляющая переменная.

local function chars(s)


return coroutine.wrap(function()
for i = 1, #s do
coroutine.yield(s:sub(i, i))
end
end)
end

for c in chars 'abcde' do

https://riptutorial.com/ru/home 39
print(c) --> a, b, c, d, e
end

Прочитайте итераторы онлайн: https://riptutorial.com/ru/lua/topic/4165/итераторы

https://riptutorial.com/ru/home 40
глава 7: метатаблицы
Синтаксис
• [[local] mt =] getmetatable ( t ) -> получить связанный метатебель для ' t '
• [[local] t =] setmetatable ( t , mt ) -> установить метатебель для ' t ' в ' mt ' и возвращает '
t'

параметры

параметр подробности

Переменная, относящаяся к таблице lua; также может быть литералом


T
таблицы.

Таблица для использования в качестве метатетируемого; может иметь


Монтана
ноль или более полей метаметода.

замечания
Есть некоторые метаметоды, не упомянутые здесь. Полный список и их использование см.
В соответствующей записи в руководстве lua .

Examples

Создание и использование метаданных

Metatable определяет набор операций, которые изменяют поведение объекта lua.


Метатизируемый - это просто обычная таблица, которая используется особым образом.

local meta = { } -- create a table for use as metatable

-- a metatable can change the behaviour of many things


-- here we modify the 'tostring' operation:
-- this fields should be a function with one argument.
-- it gets called with the respective object and should return a string
meta.__tostring = function (object)
return string.format("{ %d, %d }", object.x, object.y)
end

-- create an object
local point = { x = 13, y = -2 }
-- set the metatable
setmetatable(point, meta)

https://riptutorial.com/ru/home 41
-- since 'print' calls 'tostring', we can use it directly:
print(point) -- prints '{ 13, -2 }'

Использование таблиц в качестве метаметодов

Некоторые метаметоды не должны быть функциями. Наиболее важным примером для


этого является __index . Это также может быть таблица, которая затем используется в
качестве поиска. Это довольно часто используется при создании классов в lua. Здесь
таблица (часто метатетированная) используется для хранения всех операций (методов)
класса:

local meta = {}
-- set the __index method to the metatable.
-- Note that this can't be done in the constructor!
meta.__index = meta

function create_new(name)
local self = { name = name }
setmetatable(self, meta)
return self
end

-- define a print function, which is stored in the metatable


function meta.print(self)
print(self.name)
end

local obj = create_new("Hello from object")


obj:print()

Сборщик мусора - метатет __gc

5,2

Объекты в lua собирают мусор. Иногда вам нужно освободить некоторый ресурс, хотите
распечатать сообщение или сделать что-то еще, когда объект будет уничтожен (собран).
Для этого вы можете использовать __gc , который __gc с объектом в качестве аргумента
при уничтожении объекта. Вы могли видеть этот метаметод как своего рода деструктор.

Этот пример показывает __gc в действии. Когда внутренняя таблица, присвоенная t


получает сбор мусора, она печатает сообщение до сбора. Аналогично для внешней таблицы
при достижении конца скрипта:

local meta =
{
__gc = function(self)
print("destroying self: " .. self.name)
end
}

local t = setmetatable({ name = "outer" }, meta)


do

https://riptutorial.com/ru/home 42
local t = { name = "inner" }
setmetatable(t, meta)
end

Больше метаметодов

Есть еще много метаметодов, некоторые из них являются арифметическими (например,


сложение, вычитание, умножение), есть побитовые операции (и, или, xor, shift), сравнение
(<,>), а также операции базового типа, такие как == и # (равенство и длина). Давайте
построим класс, который поддерживает многие из этих операций: призыв к рациональной
арифметике. Хотя это очень просто, это показывает идею.

local meta = {
-- string representation
__tostring = function(self)
return string.format("%s/%s", self.num, self.den)
end,
-- addition of two rationals
__add = function(self, rhs)
local num = self.num * rhs.den + rhs.num * self.den
local den = self.den * rhs.den
return new_rational(num, den)
end,
-- equality
__eq = function(self, rhs)
return self.num == rhs.num and self.den == rhs.den
end
}

-- a function for the creation of new rationals


function new_rational(num, den)
local self = { num = num, den = den }
setmetatable(self, meta)

return self
end

local r1 = new_rational(1, 2)
print(r1) -- 1/2

local r2 = new_rational(1, 3)
print(r1 + r2) -- 5/6

local r3 = new_rational(1, 2)
print(r1 == r3) -- true
-- this would be the behaviour if we hadn't implemented the __eq metamethod.
-- this compares the actual tables, which are different
print(rawequal(r1, r3)) -- false

Сделать таблицы доступными

Существует __call , называемый __call , который определяет bevahiour объекта при


использовании в качестве функции, например object() . Это можно использовать для
создания объектов функции:

https://riptutorial.com/ru/home 43
-- create the metatable with a __call metamethod
local meta = {
__call = function(self)
self.i = self.i + 1
end,
-- to view the results
__tostring = function(self)
return tostring(self.i)
end
}

function new_counter(start)
local self = { i = start }
setmetatable(self, meta)
return self
end

-- create a counter
local c = new_counter(1)
print(c) --> 1
-- call -> count up
c()
print(c) --> 2

Метаметод вызывается с соответствующим объектом, все остальные аргументы


передаются функции после этого:

local meta = {
__call = function(self, ...)
print(self.prepend, ...)
end
}

local self = { prepend = "printer:" }


setmetatable(self, meta)

self("foo", "bar", "baz")

Индексирование таблиц

Возможно, наиболее важным использованием метатежей является возможность


изменения индексации таблиц. Для этого необходимо рассмотреть два действия: чтение
содержимого и запись содержимого таблицы. Обратите внимание, что оба действия
запускаются только в том случае, если соответствующий ключ отсутствует в таблице.

чтение

local meta = {}

-- to change the reading action, we need to set the '__index' method


-- it gets called with the corresponding table and the used key
-- this means that table[key] translates into meta.__index(table, key)
meta.__index = function(object, index)
-- print a warning and return a dummy object

https://riptutorial.com/ru/home 44
print(string.format("the key '%s' is not present in object '%s'", index, object))
return -1
end

-- create a testobject
local t = {}

-- set the metatable


setmetatable(t, meta)

print(t["foo"]) -- read a non-existent key, prints the message and returns -1

Это можно использовать для повышения ошибки при чтении несуществующего ключа:

-- raise an error upon reading a non-existent key


meta.__index = function(object, index)
error(string.format("the key '%s' is not present in object '%s'", index, object))
end

Пишу

local meta = {}

-- to change the writing action, we need to set the '__newindex' method


-- it gets called with the corresponding table, the used key and the value
-- this means that table[key] = value translates into meta.__newindex(table, key, value)
meta.__newindex = function(object, index, value)
print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
value, object, index))
--object[index] = value -- we can't do this, see below
end

-- create a testobject
local t = { }

-- set the metatable


setmetatable(t, meta)

-- write a key (this triggers the method)


t.foo = 42

Теперь вы можете спросить себя, как фактическое значение написано в таблице. В этом
случае это не так. Проблема здесь в том, что метаметоды могут вызывать метаметоды, что
приведет к инфинитивному циклу или, точнее, переполнению стека. Итак, как мы можем
это решить? Решение для этого называется доступным доступом к таблице .

Доступ к исходной таблице

Иногда вы не хотите запускать метаметоды, но на самом деле пишете или читаете именно
данный ключ, без каких-либо умных функций, обернутых вокруг доступа. Для этого lua
предоставляет вам методы доступа к исходным таблицам:

https://riptutorial.com/ru/home 45
-- first, set up a metatable that allows no read/write access
local meta = {
__index = function(object, index)
-- raise an error
error(string.format("the key '%s' is not present in object '%s'", index, object))
end,
__newindex = function(object, index, value)
-- raise an error, this prevents any write access to the table
error(string.format("you are not allowed to write the object '%s'", object))
end
}

local t = { foo = "bar" }


setmetatable(t, meta)

-- both lines raise an error:


--print(t[1])
--t[1] = 42

-- we can now circumvent this problem by using raw access:


print(rawget(t, 1)) -- prints nil
rawset(t, 1, 42) -- ok

-- since the key 1 is now valid, we can use it in a normal manner:


print(t[1])

С этим мы теперь можем переписать __newindex метод __newindex чтобы фактически


записать значение в таблицу:

meta.__newindex = function(object, index, value)


print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
value, object, index))
rawset(object, index, value)
end

Моделирование ООП

local Class = {} -- objects and classes will be tables


local __meta = {__index = Class}
-- ^ if an instance doesn't have a field, try indexing the class
function Class.new()
-- return setmetatable({}, __meta) -- this is shorter and equivalent to:
local new_instance = {}
setmetatable(new_instance, __meta)
return new_instance
end
function Class.print()
print "I am an instance of 'class'"
end

local object = Class.new()


object.print() --> will print "I am an instance of 'class'"

Методы экземпляра могут быть записаны путем передачи объекта в качестве первого
аргумента.

https://riptutorial.com/ru/home 46
-- append to the above example
function Class.sayhello(self)
print("hello, I am ", self)
end
object.sayhello(object) --> will print "hello, I am <table ID>"
object.sayhello() --> will print "hello, I am nil"

Для этого есть синтаксический сахар.

function Class:saybye(phrase)
print("I am " .. self .. "\n" .. phrase)
end
object:saybye("c ya") --> will print "I am <table ID>
--> c ya"

Мы также можем добавлять поля по умолчанию в класс.

local Class = {health = 100}


local __meta = {__index = Class}

function Class.new() return setmetatable({}, __meta) end


local object = Class.new()
print(object.health) --> prints 100
Class.health = 50; print(object.health) --> prints 50
-- this should not be done, but it illustrates lua indexes "Class"
-- when "object" doesn't have a certain field
object.health = 200 -- This does NOT index Class
print(object.health) --> prints 200

Прочитайте метатаблицы онлайн: https://riptutorial.com/ru/lua/topic/2444/метатаблицы

https://riptutorial.com/ru/home 47
глава 8: наборы
Examples

Поиск элемента в списке

Там нет встроенного способа поиска списка для определенного элемента. Однако
программирование в Lua показывает, как вы можете создать набор, который может
помочь:

function Set (list)


local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end

Затем вы можете поместить свой список в Set и test для членства:

local items = Set { "apple", "orange", "pear", "banana" }

if items["orange"] then
-- do something
end

Использование таблицы в качестве набора

Создать набор

local set = {} -- empty set

Создайте набор с элементами, установив их значение в true :

local set = {pear=true, plum=true}

-- or initialize by adding the value of a variable:


local fruit = 'orange'
local other_set = {[fruit] = true} -- adds 'orange'

Добавить участника в набор


добавьте элемент, установив его значение в true

set.peach = true
set.apple = true
-- alternatively

https://riptutorial.com/ru/home 48
set['banana'] = true
set['strawberry'] = true

Удалите элемент из набора

set.apple = nil

Использование nil вместо false для удаления «apple» из таблицы предпочтительнее,


потому что это упростит итерационные элементы. nil удаляет запись из таблицы, а false ее
значение.

Тест членства

if set.strawberry then
print "We've got strawberries"
end

Перейдем к элементам в наборе

for element in pairs(set) do


print(element)
end

Прочитайте наборы онлайн: https://riptutorial.com/ru/lua/topic/3875/наборы

https://riptutorial.com/ru/home 49
глава 9: Обработка ошибок
Examples

Использование pcall

pcallозначает «защищенный вызов». Он используется для добавления обработки ошибок в


функции. pcall работает аналогично try-catch на других языках. Преимущество pcall
заключается в том, что полное выполнение скрипта не прерывается, если возникают
ошибки в функциях, вызываемых с помощью pcall . Если ошибка внутри функции,
вызванной с помощью pcall возникает ошибка, и остальная часть кода продолжает
выполнение.

Синтаксис:

pcall( f , arg1,···)

Возвращаемые значения:

Возвращает два значения

1. status (boolean)

• Возвращает true, если функция была выполнена без ошибок.


• Возвращает false, если внутри функции возникла ошибка.

2. возвращаемое значение функции или сообщение об ошибке, если в функциональном


блоке произошла ошибка.

pcall может использоваться для различных случаев, однако общий из них заключается в
том, чтобы ловить ошибки из функции, предоставленной вашей функции. Например,
скажем, у нас есть эта функция:

local function executeFunction(funcArg, times) then


for i = 1, times do
local ran, errorMsg = pcall( funcArg )
if not ran then
error("Function errored on run " .. tostring(i) .. "\n" .. errorMsg)
end
end
end

Когда заданные ошибки функции при запуске 3, сообщение об ошибке будет понятно
пользователю, что оно не исходит от вашей функции, а от функции, которая была

https://riptutorial.com/ru/home 50
предоставлена нашей функции. Кроме того, учитывая это, фантастический BSoD может
быть уведомлен о пользователе. Однако это зависит от приложения, которое реализует
эту функцию, поскольку API, скорее всего, этого не сделает.

Пример A - Исполнение без pcall

function square(a)
return a * "a" --This will stop the execution of the code and throws an error, because of
the attempt to perform arithmetic on a string value
end

square(10);

print ("Hello World") -- This is not being executed because the script was interrupted due
to the error

Пример B - Исполнение с pcall

function square(a)
return a * "a"
end

local status, retval = pcall(square,10);

print ("Status: ", status) -- will print "false" because an error was thrown.
print ("Return Value: ", retval) -- will print "input:2: attempt to perform arithmetic on a
string value"
print ("Hello World") -- Prints "Hello World"

Пример. Выполнение безупречного кода.

function square(a)
return a * a
end

local status, retval = pcall(square,10);

print ("Status: ", status) -- will print "true" because no errors were thrown
print ("Return Value: ", retval) -- will print "100"
print ("Hello World") -- Prints "Hello World"

Обработка ошибок в Lua

Предполагая, что мы имеем следующую функцию:

function foo(tab)
return tab.a
end -- Script execution errors out w/ a stacktrace when tab is not a table

Давайте немного поправим

function foo(tab)

https://riptutorial.com/ru/home 51
if type(tab) ~= "table" then
error("Argument 1 is not a table!", 2)
end
return tab.a
end -- This gives us more information, but script will still error out

Если мы не хотим, чтобы функция выходила из строя в программе даже в случае ошибки, в
lua стандартно выполнять следующие действия:

function foo(tab)
if type(tab) ~= "table" then return nil, "Argument 1 is not a table!" end
return tab.a
end -- This never crashes the program, but simply returns nil and an error message

Теперь у нас есть функция, которая ведет себя так, мы можем делать такие вещи:

if foo(20) then print(foo(20)) end -- prints nothing


result, error = foo(20)
if result then print(result) else log(error) end

И если мы хотим, чтобы программа потерпела крах, если что-то пошло не так, мы все равно
можем это сделать:

result, error = foo(20)


if not result then error(error) end

К счастью, нам даже не нужно писать все это каждый раз; lua имеет функцию, которая
выполняет именно это

result = assert(foo(20))

Прочитайте Обработка ошибок онлайн: https://riptutorial.com/ru/lua/topic/4561/обработка-


ошибок

https://riptutorial.com/ru/home 52
глава 10: Объект-ориентация
Вступление
Сам Lua не предлагает систему классов. Однако возможно реализовать классы и объекты
в виде таблиц с помощью нескольких трюков.

Синтаксис
• function <class>.new() return setmetatable({}, {__index=<class>}) end

Examples

Простая ориентация объекта

Вот базовый пример того, как сделать очень простую систему классов

Class = {}
local __instance = {__index=Class} -- Metatable for instances
function Class.new()
local instance = {}
setmetatable(instance, __instance)
return instance
-- equivalent to: return setmetatable({}, __instance)
end

Чтобы добавить переменные и / или методы, просто добавьте их в класс. Оба могут быть
переопределены для каждого экземпляра.

Class.x = 0
Class.y = 0
Class:getPosition()
return {self.x, self.y}
end

А для создания экземпляра класса:

object = Class.new()

или же

setmetatable(Class, {__call = Class.new}


-- Allow the class itself to be called like a function
object = Class()

И использовать его:

https://riptutorial.com/ru/home 53
object.x = 20
-- This adds the variable x to the object without changing the x of
-- the class or any other instance. Now that the object has an x, it
-- will override the x that is inherited from the class
print(object.x)
-- This prints 20 as one would expect.
print(object.y)
-- Object has no member y, therefore the metatable redirects to the
-- class table, which has y=0; therefore this prints 0
object:getPosition() -- returns {20, 0}

Изменение метаметодов объекта

имеющий

local Class = {}
Class.__meta = {__index=Class}
function Class.new() return setmetatable({}, Class.__meta)

Предполагая, что мы хотим изменить поведение одного экземпляра object = Class.new()


используя метатебель,

есть несколько ошибок, которых следует избегать:

setmetatable(object, {__call = table.concat}) -- WRONG

Это заменяет старый метатебель на новый, поэтому разбивает наследование класса

getmetatable(object).__call = table.concat -- WRONG AGAIN

Имейте в виду, что таблицы «значения» являются только ссылкой; на самом деле
существует только одна фактическая таблица для всех экземпляров объекта, если
конструктор не определен как 1 , поэтому, делая это, мы модифицируем поведение всех
экземпляров класса.

Один правильный способ сделать это:

Без изменения класса:

setmetatable(
object,
setmetatable(
{__call=table.concat},
{__index=getmetatable(object)}
)
)

Как это работает? - Мы создаем новый метатебель, как в ошибке №1, но вместо того,
чтобы оставить его пустым, мы создаем мягкую копию оригинального метатега. Можно

https://riptutorial.com/ru/home 54
сказать, что новый метатебель «наследует» от исходного, как если бы он был экземпляром
класса. Теперь мы можем переопределять значения исходного метатета без их
модификации.

Изменение класса:

1-й (рекомендуется):

local __instance_meta = {__index = Class.__meta}


-- metatable for the metatable
-- As you can see, lua can get very meta very fast
function Class.new()
return setmetatable({}, setmetatable({}, __instance_meta))
end

2-й (менее рекомендуется): см. 1

1 function Class.new() return setmetatable({}, {__index=Class}) end

Прочитайте Объект-ориентация онлайн: https://riptutorial.com/ru/lua/topic/8908/объект-


ориентация

https://riptutorial.com/ru/home 55
глава 11: Сборщик мусора и слабые столы
Синтаксис
1. collectgarbage (gcrule [, gcdata]) - собирать мусор, используя gcrule
2. setmetatable (tab, {__mode = weakmode}) - установить слабый режим вкладки в слабом
режиме

параметры

параметр подробности

Действие для gc (сборщик мусора): "stop" (остановить сбор), "restart" (


начать собирать снова), "collect" или « nil (собрать весь мусор), "step" (
gcrule &
сделать один шаг сбора), "count" ( return count of used memory в KBs),
gcdata
"setpause" и данные - от 0 % до 100 % (параметр установки паузы в gc),
"setstepmul" а данные - от 0 % до 100 (установите "stepmul" для gc) ,

Тип слабой таблицы: "k" (только слабые клавиши), "v" (только слабые
weakmode
значения), "vk" (слабые клавиши и значения)

Examples

Слабые столы

local t1, t2, t3, t4 = {}, {}, {}, {} -- Create 4 tables


local maintab = {t1, t2} -- Regular table, strong references to t1 and t2
local weaktab = setmetatable({t1, t2, t3, t4}, {__mode = 'v'}) -- table with weak references.

t1, t2, t3, t4 = nil, nil, nil, nil -- No more "strong" references to t3 and t4
print(#maintab, #weaktab) --> 2 4

collectgarbage() -- Destroy t3 and t4 and delete weak links to them.


print(#maintab, #weaktab) --> 2 2

Прочитайте Сборщик мусора и слабые столы онлайн: https://riptutorial.com/ru/lua/topic/5769/


сборщик-мусора-и-слабые-столы

https://riptutorial.com/ru/home 56
глава 12: Согласование образцов
Синтаксис
• string.find (str, pattern [, init [, plain]]) - возвращает начальный и конечный индекс
соответствия в str

• string.match (str, pattern [, index]) - Соответствует шаблону один раз (начиная с


индекса)

• string.gmatch (str, pattern) - возвращает функцию, которая выполняет итерацию через


все совпадения в str

• string.gsub (str, pattern, repl [, n]) - Заменяет подстроки (максимум до n раз)

• . представляет все символы

• %a обозначает все буквы

• %l обозначает все строчные буквы

• %u представляет все прописные буквы

• %d все цифры

• %x представляет все шестнадцатеричные цифры

• %s представляет все пробельные символы

• %p обозначает все знаки пунктуации

• %g представляет все печатные символы, кроме пробела

• %c все управляющие символы

• [set] представляет класс, который является объединением всех символов в наборе.

• [^set] представляет собой дополнение к множеству

• * жадный матч 0 или более вхождений предыдущего класса символов

• + жадный матч 1 или более вхождений предыдущего класса символов

• - ленивое совпадение 0 или более случаев предыдущего символьного класса

• ? соответствие точно 0 или 1 вхождения предыдущего символьного класса

https://riptutorial.com/ru/home 57
замечания
В некоторых примерах используется нотация (<string literal>):function <string literal> ,
которая эквивалентна string.function(<string literal>, <string literal>) потому что все
строки имеют метатебель с __index полем __index к таблице string .

Examples

Соответствие Lua

Вместо использования регулярного выражения в строковой библиотеке Lua имеется


специальный набор символов, используемых в синтаксических совпадениях. Оба варианта
могут быть очень похожими, но соответствие шаблону Lua более ограничено и имеет
другой синтаксис. Например, последовательность символов %a соответствует любой букве,
в то время как ее версия в верхнем регистре представляет все символы без букв , все
классы символов (последовательность символов, которая, как шаблон, может
соответствовать набору элементов), перечислены ниже.

Класс символов Раздел соответствия

%в буквы (AZ, az)

%с управляющие символы (\ n, \ t, \ r, ...)

%d цифры (0-9)

%л строчная буква (az)

%п знаки пунктуации (!,?, &, ...)

%s символы пробела

%U заглавные буквы

% мас буквенно-цифровые символы (AZ, az, 0-9)

%Икс шестнадцатеричные цифры (\ 3, \ 4, ...)

%г символ с представлением 0

, Соответствует любому персонажу

Как упоминалось выше, любая версия этого класса в верхнем регистре представляет
дополнение к классу. Например, %D будет соответствовать любой несимметричной
символьной последовательности:

https://riptutorial.com/ru/home 58
string.match("f123", "%D") --> f

В дополнение к классам символов некоторые символы имеют специальные функции как


шаблоны:

( ) % . + - * [ ? ^ $

Символ % представляет escape-символ, делая %? соответствие опросу и %% соответствуют


символу процента. Вы можете использовать символ % с любым другим алфавитно-
цифровым символом, поэтому, если вам нужно убежать, например, цитату, вы должны
использовать \\ перед ней, которая избегает любого символа из строки lua.

Набор символов, представленный внутри квадратных скобок ( [] ), позволяет создать


специальный класс символов, сочетающий разные классы и отдельные символы:

local foo = "bar123bar2341"


print(foo:match "[arb]") --> b

Вы можете получить дополнение набора символов, начиная его с ^ :

local foo = "bar123bar2341"


print(string.match(foo, "[^bar]")) --> 1

В этом примере string.match найдет первое вхождение, которое не является b , a или r .

Шаблоны могут быть более полезными с помощью повторений / необязательных


модификаторов, шаблоны в lua предлагают эти четыре символа:

символ Модификатор

+ Одно или несколько повторений

* Ноль или больше повторений

- Также ноль или более повторений

? Необязательный (ноль или один случай)

Символ + представляет один или несколько совпадающих символов в последовательности


и всегда будет возвращать самую длинную согласованную последовательность:

local foo = "12345678bar123"


print(foo:match "%d+") --> 12345678

Как вы можете видеть, * похоже на + , но он принимает нулевые вхождения символов и


обычно используется для сопоставления необязательных пробелов между разными

https://riptutorial.com/ru/home 59
шаблонами.

Символ - также похож на * , но вместо того, чтобы возвращать самую длинную


согласованную последовательность, он соответствует кратчайшему.

Модификатор ? соответствует необязательному символу, что позволяет вам сопоставить,


например, отрицательную цифру:

local foo = "-20"


print(foo:match "[+-]?%d+")

Механизм соответствия шаблону Lua предоставляет несколько дополнительных элементов,


соответствующих шаблону:

Персонаж Описание

%n для n от 1 до 9 соответствует подстроке, равной n-ой захваченной строке

соответствует подстроке между двумя разными символами


%bxy
(сбалансированная пара x и y )

frontier pattern: соответствует пустой строке в любой позиции, так что


следующий символ
%f[set]
принадлежит множеству, а предыдущий символ не принадлежит
множеству

string.find (Введение)

Функция find

Сначала давайте посмотрим на функцию string.find :

Функция string.find (s, substr [, init [, plain]]) возвращает начальный и конечный


индекс подстроки , если найдено, и ноль в противном случае, начиная с индекса init , если
это предусмотрено ( по умолчанию 1).

("Hello, I am a string"):find "am" --> returns 10 11


-- equivalent to string.find("Hello, I am a string", "am") -- see remarks

Представление шаблонов
("hello world"):find ".- " -- will match characters until it finds a space
--> so it will return 1, 6

https://riptutorial.com/ru/home 60
Все, кроме следующих символов, представляют собой ^$()%.[]*+-?) . Любой из этих
символов может быть представлен символом % после самого символа.

("137'5 m47ch s0m3 d1g175"):find "m%d%d" -- will match an m followed by 2 digit


--> this will match m47 and return 7, 9

("stack overflow"):find "[abc]" -- will match an 'a', a 'b' or a 'c'


--> this will return 3 (the A in stAck)

("stack overflow"):find "[^stack ]"


-- will match all EXCEPT the letters s, t, a, c and k and the space character
--> this will match the o in overflow

("hello"):find "o%d?" --> matches o, returns 5, 5


("hello20"):find "o%d?" --> matches o2, returns 5, 6
-- the ? means the character is optional

("helllllo"):find "el+" --> will match elllll


("heo"):find "el+" --> won't match anything

("helllllo"):find "el*" --> will match elllll


("heo"):find "el*" --> will match e

("helelo"):find "h.+l" -- + will match as much as it gets


--> this matches "helel"
("helelo"):find "h.-l" -- - will match as few as it can
--> this wil only match "hel"

("hello"):match "o%d*"
--> like ?, this matches the "o", because %d is optional
("hello20"):match "o%d*"
--> unlike ?, it maches as many %d as it gets, "o20"
("hello"):match "o%d"
--> wouldn't find anything, because + looks for 1 or more characters

Функция `gmatch`

Как это устроено


Функция string.gmatch примет входную строку и шаблон. Этот шаблон описывает, что на
самом деле получить обратно. Эта функция вернет функцию, которая фактически
является итератором. Результат этого итератора будет соответствовать шаблону.

type(("abc"):gmatch ".") --> returns "function"

for char in ("abc"):gmatch "." do


print char -- this prints:
--> a
--> b
--> c
end

for match in ("#afdde6"):gmatch "%x%x" do


print("#" .. match) -- prints:

https://riptutorial.com/ru/home 61
--> #af
--> #dd
--> #e6
end

Представляем захваты:
Это очень похоже на регулярную функцию, однако она вернет только захваты вместо
полного соответствия.

for key, value in ("foo = bar, bar=foo"):gmatch "(%w+)%s*=%s*(%w+)" do


print("key: " .. key .. ", value: " .. value)
--> key: foo, value: bar
--> key: bar, value: foo
end

Функция gsub

не путайте с функцией string.sub, которая возвращает подстроку!

Как это устроено


строковый аргумент

("hello world"):gsub("o", "0")


--> returns "hell0 w0rld", 2
-- the 2 means that 2 substrings have been replaced (the 2 Os)

("hello world, how are you?"):gsub("[^%s]+", "word")


--> returns "word word, word word word?", 5

("hello world"):gsub("([^%s])([^%s]*)", "%2%1")


--> returns "elloh orldw", 2

аргумент функции

local word = "[^%s]+"

function func(str)
if str:sub(1,1):lower()=="h" then
return str
else
return "no_h"
end
end
("hello world"):gsub(word, func)
--> returns "hello no_h", 2

https://riptutorial.com/ru/home 62
аргумент таблицы

local word = "[^%s]+"

sub = {}
sub["hello"] = "g'day"
sub["world"] = "m8"

("hello world"):gsub(word, sub)


--> returns "g'day m8"

("hello world, how are you?"):gsub(word, sub)


--> returns "g'day m8, how are you?"
-- words that are not in the table are simply ignored

Прочитайте Согласование образцов онлайн: https://riptutorial.com/ru/lua/topic/5829/


согласование-образцов

https://riptutorial.com/ru/home 63
глава 13: Создание и использование
модулей
замечания
Основной шаблон для написания модуля - заполнить таблицу ключами, которые являются
именами функций и значениями, которые являются самими функциями. Затем модуль
возвращает эту функцию для вызова кода, который require и использует. (Функции
являются первоклассными значениями в Lua, поэтому хранение функции в таблице
является простым и обычным.) Таблица также может содержать любые важные константы
в форме, скажем, строк или чисел.

Examples

Написание модуля

--- trim: a string-trimming module for Lua


-- Author, date, perhaps a nice license too
--
-- The code here is taken or adapted from material in
-- Programming in Lua, 3rd ed., Roberto Ierusalimschy

-- trim_all(string) => return string with white space trimmed on both sides
local trim_all = function (s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end

-- trim_left(string) => return string with white space trimmed on left side only
local trim_left = function (s)
return (string.gsub(s, "^%s*(.*)$", "%1"))
end

-- trim_right(string) => return string with white space trimmed on right side only
local trim_right = function (s)
return (string.gsub(s, "^(.-)%s*$", "%1"))
end

-- Return a table containing the functions created by this module


return {
trim_all = trim_all,
trim_left = trim_left,
trim_right = trim_right
}

Альтернативный подход к приведенному выше - создать таблицу верхнего уровня, а затем


сохранить функции непосредственно в ней. В этом идиоме наш модуль выше будет
выглядеть так:

https://riptutorial.com/ru/home 64
-- A conventional name for the table that will hold our functions
local M = {}

-- M.trim_all(string) => return string with white space trimmed on both sides
function M.trim_all(s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end

-- M.trim_left(string) => return string with white space trimmed on left side only
function M.trim_left(s)
return (string.gsub(s, "^%s*(.*)$", "%1"))
end

-- trim_right(string) => return string with white space trimmed on right side only
function M.trim_right(s)
return (string.gsub(s, "^(.-)%s*$", "%1"))
end

return M

С точки зрения звонящего, между этими двумя стилями мало различий. (Один из
достоинств, о котором стоит упомянуть, заключается в том, что первый стиль усложняет
пользователям возможность обезвреживать модуль. Это либо pro, либо con, в зависимости
от вашей точки зрения. Подробнее об этом см. В этом блоге Энрике Гарсиа Cota.)

Использование модуля

-- The following assumes that trim module is installed or in the caller's package.path,
-- which is a built-in variable that Lua uses to determine where to look for modules.
local trim = require "trim"

local msg = " Hello, world! "


local cleaned = trim.trim_all(msg)
local cleaned_right = trim.trim_right(msg)
local cleaned_left = trim.trim_left(msg)

-- It's also easy to alias functions to shorter names.


local trimr = trim.trim_right
local triml = trim.trim_left

Прочитайте Создание и использование модулей онлайн:


https://riptutorial.com/ru/lua/topic/1148/создание-и-использование-модулей

https://riptutorial.com/ru/home 65
глава 14: Сопрограммы
Синтаксис
• coroutine.create (function) возвращает сопрограмму (type (coroutine) == 'thread'),
содержащую эту функцию.

• coroutine.resume (co, ...) возобновить или запустить сопрограмму. Любые


дополнительные аргументы, возвращаемые резюме, возвращаются из coroutine.yield (),
которые ранее приостановили сопрограмму. Если сопрограмма не была запущена,
дополнительные аргументы становятся аргументами функции.

• coroutine.yield (...) дает текущую исполняемую копию. Выполнение выполняет


резервное копирование после вызова coroutine.resume (), который начал эту
сопрограмму. Любые аргументы, даваемые уроку, возвращаются из соответствующей
сопрограммы coroutine.resume (), которая запускает сопрограмму.

• coroutine.status (co) возвращает статус сопрограммы, которая может быть:

○ «dead»: функция в сопрограмме дошла до конца, и сопрограмма не может быть


возобновлена больше
○ «running»: сопрограмма продолжена и работает
○ «normal»: сопрограмма возобновила другую сопрограмму
○ «приостановлено»: сопротекция уступила и ждет возобновления

• Функция coroutine.wrap (function) возвращает функцию, которая при вызове


возобновляет сопрограмму, которая была бы создана командой coroutine.create
(function).

замечания
Система coroutine была реализована в lua для эмуляции многопоточности, существующей
на других языках. Он работает, переключаясь с чрезвычайно высокой скоростью между
различными функциями, чтобы пользователь-пользователь считал, что они выполняются
одновременно.

Examples

Создание и использование сопрограммы

Все функции для взаимодействия с сопрограммами доступны в таблице сопрограмм .


Новая сопрограмма создается с помощью функции coroutine.create с единственным

https://riptutorial.com/ru/home 66
аргументом: функция с исполняемым кодом:

thread1 = coroutine.create(function()
print("honk")
end)

print(thread1)
-->> thread: 6b028b8c

Объект coroutine возвращает значение типа thread , представляющего новую сопрограмму.


Когда создается новая сопрограмма, ее начальное состояние приостанавливается:

print(coroutine.status(thread1))
-->> suspended

Чтобы возобновить или запустить сопрограмму, используется функция coroutine.resume ,


первый аргумент - объект потока:

coroutine.resume(thread1)
-->> honk

Теперь сопрограмма выполняет код и завершается, меняя свое состояние на мертвое ,


которое невозможно возобновить.

print(coroutine.status(thread1))
-->> dead

Coroutines может приостановить его выполнение и возобновить его позже благодаря


функции coroutine.yield :

thread2 = coroutine.create(function()
for n = 1, 5 do
print("honk "..n)
coroutine.yield()
end
end)

Как вы можете видеть, coroutine.yield () присутствует внутри цикла for, теперь, когда мы
возобновляем сопрограмму coroutine, он будет выполнять код, пока не достигнет
coroutine.yield:

coroutine.resume(thread2)
-->> honk 1
coroutine.resume(thread2)
-->> honk 2

По завершении цикла состояние потока становится мертвым и не может быть


возобновлено. Coroutines также позволяет обмен данными:

https://riptutorial.com/ru/home 67
thread3 = coroutine.create(function(complement)
print("honk "..complement)
coroutine.yield()
print("honk again "..complement)
end)
coroutine.resume(thread3, "stackoverflow")
-->> honk stackoverflow

Если сопрограмма выполняется снова без дополнительных аргументов, дополнение будет


по-прежнему аргументом из первого резюме, в данном случае «stackoverflow»:

coroutine.resume(thread3)
-->> honk again stackoverflow

Наконец, когда coroutine заканчивается, любые значения, возвращаемые его функцией,


переходят к соответствующему резюме:

thread4 = coroutine.create(function(a, b)
local c = a+b
coroutine.yield()
return c
end)
coroutine.resume(thread4, 1, 2)
print(coroutine.resume(thread4))
-->> true, 3

В этой функции используются Coroutines для передачи значений обратно в вызывающий


поток из глубины рекурсивного вызова.

local function Combinations(l, r)


local ll = #l
r = r or ll
local sel = {}
local function rhelper(depth, last)
depth = depth or 1
last = last or 1
if depth > r then
coroutine.yield(sel)
else
for i = last, ll - (r - depth) do
sel[depth] = l[i]
rhelper(depth+1, i+1)
end
end
end
return coroutine.wrap(rhelper)
end

for v in Combinations({1, 2, 3}, 2) do


print("{"..table.concat(v, ", ").."}")
end
--> {1, 2}
--> {1, 3}
--> {2, 3}

https://riptutorial.com/ru/home 68
Coroutines также может использоваться для ленивой оценки.

-- slices a generator 'c' taking every 'step'th output from the generator
-- starting at the 'start'th output to the 'stop'th output
function slice(c, start, step, stop)
local _
return coroutine.wrap(function()
for i = 1, start-1 do
_ = c()
end
for i = start, stop do
if (i - start) % step == 0 then
coroutine.yield(c())
else
_ = c()
end
end
end)
end

local alphabet = {}
for c = string.byte('a'), string.byte('z') do
alphabet[#alphabet+1] = string.char(c)
end
-- only yields combinations 100 through 102
-- requires evaluating the first 100 combinations, but not the next 5311633
local s = slice(Combinations(alphabet, 10), 100, 1, 102)
for i in s do
print(table.concat(i))
end
--> abcdefghpr
--> abcdefghps
--> abcdefghpt

Coroutines можно использовать для конструкций трубопроводов, как описано в «


Программирование в Lua» . Автор PIL, Роберто Иерусульсамчий, также опубликовал
статью об использовании сопрограмм для внедрения более совершенной и общей механики
управления потоком, например продолжения.

Прочитайте Сопрограммы онлайн: https://riptutorial.com/ru/lua/topic/3410/сопрограммы

https://riptutorial.com/ru/home 69
глава 15: таблицы
Синтаксис
• ipairs (numeric_table) - таблица Lua с числовыми индексами итератор
• пары (input_table) - общий итератор таблицы Lua
• key, value = next (input_table, input_key) - Селектор значений таблицы Lua
• table.insert (input_table, [position], value) - вставить указанное значение во входную
таблицу
• removed_value = table.remove (input_table, [position]) - pop last или удалить значение,
заданное положением

замечания
Таблицы - единственная встроенная структура данных, доступная в Lua. Это либо
элегантная простота, либо запутанность, в зависимости от того, как вы на это смотрите.

Таблица Lua представляет собой набор пар ключ-значение, где ключи уникальны, и ни
ключ, ни значение не равны nil . Таким образом, таблица Lua может напоминать словарь,
hashmap или ассоциативный массив с других языков. Многие структурные шаблоны могут
быть построены с использованием таблиц: стеков, очередей, наборов, списков, графиков и
т. Д. Наконец, таблицы могут использоваться для создания классов в Lua и для создания
модульной системы.

Lua не применяет никаких конкретных правил использования таблиц. Элементы,


содержащиеся в таблице, могут быть смесью типов Lua. Так, например, одна таблица
может содержать строки, функции, логические значения, числа и даже другие таблицы
как значения или ключи.

Говорят, что таблица Lua с последовательными положительными целыми ключами,


начинающимися с 1, имеет последовательность. Ключи-значения с положительными
целыми ключами являются элементами последовательности. Другие языки называют это
массивом на основе 1. Некоторые стандартные операции и функции работают только с
последовательностью таблицы, а некоторые имеют недетерминированное поведение при
применении к таблице без последовательности.

Установка значения в таблице на nil удаляет его из таблицы. Итераторы больше не будут
видеть связанный ключ. При кодировании таблицы с последовательностью важно не
нарушать последовательность; Удалите только последний элемент или используйте
функцию, например, стандартную table.remove , которое перемещает элементы вниз, чтобы
закрыть пробел.

https://riptutorial.com/ru/home 70
Examples

Создание таблиц

Создание пустой таблицы так же просто:

local empty_table = {}

Вы также можете создать таблицу в виде простого массива:

local numeric_table = {
"Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.

Имейте в виду, что по умолчанию индексирование таблицы начинается с 1.

Также возможно создание таблицы с ассоциативными элементами:

local conf_table = {
hostname = "localhost",
port = 22,
flags = "-Wall -Wextra"
clients = { -- nested table
"Eve", "Jim", "Peter"
}
}

Использование выше - синтаксический сахар для того, что ниже. Ключи в этом экземпляре
относятся к типу, строке. Вышеупомянутый синтаксис был добавлен, чтобы таблицы
отображались как записи. Этот синтаксис стиля записи параллелен синтаксису
индексирования таблиц со строковыми ключами, как показано в учебнике «Основное
использование».

Как поясняется в разделе замечаний, синтаксис стиля записи не работает для каждого
возможного ключа. Кроме того, ключ может быть любым значением любого типа, а
предыдущие примеры охватывают только строки и порядковые номера. В других случаях
вам нужно будет использовать явный синтаксис:

local unique_key = {}
local ops_table = {
[unique_key] = "I'm unique!"
["^"] = "power",
[true] = true
}

Итерирующие столы

https://riptutorial.com/ru/home 71
Стандартная библиотека Lua предоставляет функцию pairs которая выполняет итерации
по ключам и значениям таблицы. При повторении с pairs нет определенного порядка для
обхода, даже если ключи таблицы являются числовыми .

for key, value in pairs(input_table) do


print(key, " -- ", value)
end

Для таблиц с использованием цифровых клавиш Lua предоставляет функцию ipairs .


Функция ipairs всегда будет перебираться из table[1] , table[2] и т. Д. До тех пор, пока не
будет найдено первое значение nil .

for index, value in ipairs(numeric_table) do


print(index, ". ", value)
end

Будьте ipairs() что итерация с помощью ipairs() не будет работать, как вы могли бы
захотеть в нескольких случаях:

• имеет в ней «дыры». (Дополнительную информацию см. В разделе


input_table
«Избегайте пробелов в таблицах, используемых в качестве массивов»). Например:

table_with_holes = {[1] = "value_1", [3] = "value_3"}

• ключи были не все числовые. Например:

mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}

Разумеется, для таблицы, которая является правильной последовательностью, работает


следующее:

for i = 1, #numeric_table do
print(i, ". ", numeric_table[i])
end

Итерирование числовой таблицы в обратном порядке легко:

for i = #numeric_table, 1, -1 do
print(i, ". ", numeric_table[i])
end

Последний способ перебора таблиц - использовать next селектор в родовом for цикла .
Подобно pairs нет определенного порядка для обхода. (The pairs метод использует next
внутри. Таким образом , используя next по существу более ручной вариант pairs . См pairs в
справочном руководстве по next Lua в и next в справочном руководстве по Lua в для более
подробной информации.)

https://riptutorial.com/ru/home 72
for key, value in next, input_table do
print(key, value)
end

Основное использование

Основное использование таблиц включает в себя доступ и назначение элементов таблицы,


добавление содержимого таблицы и удаление содержимого таблицы. Эти примеры
предполагают, что вы знаете, как создавать таблицы.

Доступ к элементам

Учитывая следующую таблицу,

local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",


"Diarrhea", cure = "Pepto Bismol"}

Можно индексировать последовательную часть таблицы, используя синтаксис индекса,


аргумент синтаксиса индекса является ключом желаемой пары ключ-значение. Как
объясняется в учебном пособии по созданию, большая часть синтаксиса объявления
является синтаксическим сахаром для объявления пар ключ-значение. Последовательно
включенные элементы, такие как первые пять значений в example_table , используют в
качестве ключей увеличение целых значений; синтаксис записи использует имя поля в
виде строки.

print(example_table[2]) --> Heartburn


print(example_table["cure"]) --> Pepto Bismol

Для строковых ключей используется синтаксический сахар для параллелизма синтаксиса


стиля записи для строковых ключей в создании таблицы. Следующие две строки
эквивалентны.

print(example_table.cure) --> Pepto Bismol


print(example_table["cure"]) --> Pepto Bismol

Вы можете получить доступ к таблицам с помощью ключей, которые вы ранее не


использовали, это не ошибка, как на других языках. При этом возвращается значение по
умолчанию nil .

Назначение элементов

Вы можете изменить существующие элементы таблицы, назначив таблице с помощью


синтаксиса индекса. Кроме того, синтаксис индексации в стиле записи доступен для
установки значений

example_table.cure = "Lots of water, the toilet, and time"


print(example_table.cure) --> Lots of water, the toilet, and time

https://riptutorial.com/ru/home 73
example_table[2] = "Constipation"
print(example_table[2]) --> Constipation

Вы также можете добавить новые элементы в существующую таблицу с помощью


назначения.

example_table.copyright_holder = "Procter & Gamble"


example_table[100] = "Emergency source of water"

Специальное замечание. Некоторые строки не поддерживаются синтаксисом записи.


Подробности см. В разделе примечаний.

Удаление элементов

Как указано ранее, значение по умолчанию для ключа без присвоенного значения равно
nil . Удаление элемента из таблицы так же просто, как сброс значения ключа до значения
по умолчанию.

example_table[100] = "Face Mask"

Элементы теперь неотличимы от неустановленного элемента.

Длина стола

Таблицы - это просто ассоциативные массивы (см. Примечания), но когда используются


смежные целочисленные ключи, начиная с 1, говорят, что таблица имеет
последовательность .

Поиск длины части последовательности таблицы выполняется с помощью # :

local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table) --> 8

Вы можете использовать операцию длины, чтобы легко добавлять элементы в таблицу


последовательностей.

example_table[#example_table+1] = 'a'
print(#example_table) --> 9

В приведенном выше примере предыдущее значение #example_table равно 8 , добавление 1


дает вам следующий действительный целочисленный ключ в последовательности 9 ,
поэтому ... example_table[9] = 'a' . Это работает для любой длины таблицы.

Специальное замечание: использование целочисленных ключей, которые не являются


смежными и начиная с 1 разрывает последовательность, превращая таблицу в
разреженную таблицу . В этом случае результат операции длины не определен. См. Раздел

https://riptutorial.com/ru/home 74
замечаний.

Использование функций библиотеки таблиц для добавления / удаления элементов

Другим способом добавления элементов в таблицу является table.insert() . Функция


вставки работает только с таблицами последовательностей. Существует два способа
вызова функции. В первом примере показано первое использование, где указывается
индекс для вставки элемента (второй аргумент). Это подталкивает все элементы из
данного индекса к #table в одну позицию. Второй пример показывает другое
использование table.insert() , где индекс не указан, и данное значение добавляется в
конец таблицы (индекс # #table + 1 ).

local t = {"a", "b", "d", "e"}


table.insert(t, 3, "c") --> t = {"a", "b", "c", "d", "e"}

t = {"a", "b", "c", "d"}


table.insert(t, "e") --> t = {"a", "b", "c", "d", "e"}

Параллельный table.insert() для удаления элементов - table.remove() . Точно так же он


имеет две вызывающие семантики: одну для удаления элементов в заданной позиции, а
другую для удаления из конца последовательности. При удалении из середины
последовательности все следующие элементы сдвигаются вниз по одному индексу.

local t = {"a", "b", "c", "d", "e"}


local r = table.remove(t, 3) --> t = {"a", "b", "d", "e"}, r = "c"

t = {"a", "b", "c", "d", "e"}


r = table.remove(t) --> t = {"a", "b", "c", "d"}, r = "e"

Эти две функции мутируют данную таблицу. Как вы могли бы сказать второй метод вызова
table.insert() и table.remove() предоставляет семантику стека таблицам. Используя это, вы
можете написать код, как показано ниже.

function shuffle(t)
for i = 0, #t-1 do
table.insert(t, table.remove(t, math.random(#t-i)))
end
end

Он реализует «Фишер-Йейтс», возможно, неэффективно. Он использует table.insert()


чтобы добавить случайно извлеченный элемент в конец той же таблицы, а table.remove()
для случайного извлечения элемента из оставшейся неповрежденной части таблицы.

Избегать пробелов в таблицах, используемых в качестве массивов

Определение наших условий

https://riptutorial.com/ru/home 75
Под массивом здесь мы понимаем таблицу Lua, используемую в качестве
последовательности. Например:

-- Create a table to store the types of pets we like.


local pets = {"dogs", "cats", "birds"}

Мы используем эту таблицу как последовательность: группу элементов, на которые


ссылаются целые числа. Многие языки называют этот массив, и мы тоже. Но, строго
говоря, в Lua нет такой вещи, как массив. Существуют только таблицы, некоторые из
которых являются подобными массиву, некоторые из которых являются хэш-подобными
(или похожими на словари, если вы предпочитаете), а некоторые из них смешаны.

Важным моментом в нашем массиве pets является отсутствие пробелов. Первым пунктом,
pets[1] , является строка «собаки», второй пункт, pets[2] , это строка «кошки», а последний
предмет, pets[3] , - «птицы». Стандартная библиотека Lua и большинство модулей,
написанных для Lua, принимают 1 как первый индекс для последовательностей. Таким
образом, безщеточный массив имеет элементы из 1..n не пропуская никаких чисел в
последовательности. (В предельном случае n = 1 , и массив имеет только один элемент в
нем).

Lua предоставляет встроенные функции ipairs для перебора таких таблиц.

-- Iterate over our pet types.


for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end

Это напечатает «Позиция в позиции 1 - это собаки». «Позиция в позиции 2 - это кошки».
«Пункт в позиции 3 - птицы».

Но что произойдет, если мы сделаем следующее?

local pets = {"dogs", "cats", "birds"}


pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end

Массив, такой как этот второй пример, является разреженным массивом. В


последовательности есть пробелы. Этот массив выглядит так:

{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1 2 3 4 5 6 7 8 9 10 11 12

Значения nil не занимают никакой дополнительной памяти; внутренне lua сохраняет только
значения [1] = "dogs" , [2] = "cats" , [3] = "birtds" и [12] = "goldfish"

Чтобы ответить на ближайший вопрос, ipairs остановятся после птиц; «Золотая рыбка» у

https://riptutorial.com/ru/home 76
pets[12]никогда не будет достигнута, если мы не скорректируем наш код. Это связано с
тем, что ipairs выполняет ipairs с 1..n-1 где n - позиция первого найденного nil . Lua
определяет table[length-of-table + 1] равной nil . Поэтому в правильной
последовательности итерация останавливается, когда Lua пытается получить, скажем,
четвертый элемент в массиве из трех элементов.

Когда?
Два наиболее распространенных места для возникновения проблем с разреженными
массивами: (i) при попытке определить длину массива и (ii) при попытке выполнить
итерацию по массиву. Особенно:

• При использовании оператора длины # так как оператор длины останавливается при
первом найденном nil .
• При использовании функции ipairs() поскольку, как упоминалось выше, она
прекращает итерацию при обнаружении первого nil .
• При использовании функции table.unpack() поскольку этот метод останавливает
распаковку при обнаружении первого nil .
• При использовании других функций, которые (прямо или косвенно) доступны для
любого из вышеперечисленных.

Чтобы избежать этой проблемы, важно написать свой код, чтобы, если вы ожидаете, что
таблица будет массивом, вы не вводите пробелы. Пробелы можно вводить несколькими
способами:

• Если вы добавите что-то в массив в неправильном положении.


• Если вы вставляете значение nil в массив.
• Если вы удалите значения из массива.

Вы можете подумать: «Но я никогда не сделаю ничего из этого». Ну, не намеренно, но вот
конкретный пример того, как все может пойти не так. Представьте, что вы хотите написать
метод фильтрации для Lua, например, Ruby's select и Perl's grep . Метод примет тестовую
функцию и массив. Он выполняет итерацию по массиву, в свою очередь вызывает метод
тестирования по каждому элементу. Если элемент проходит, то этот элемент добавляется
в массив результатов, который возвращается в конце метода. Ниже приведена
некорректная реализация:

local filter = function (fun, t)


local res = {}
for idx, item in ipairs(t) do
if fun(item) then
res[idx] = item
end
end

return res
end

https://riptutorial.com/ru/home 77
Проблема в том, что когда функция возвращает false , мы пропускаем число в
последовательности. Представьте, что вызывающий filter(isodd, {1,2,3,4,5,6,7,8,9,10}) : в
возвращаемой таблице будут пробелы каждый раз, когда в массиве, переданном filter ,
есть четное число.

Вот фиксированная реализация:

local filter = function (fun, t)


local res = {}
for _, item in ipairs(t) do
if fun(item) then
res[#res + 1] = item
end
end

return res
end

подсказки
1. Используйте стандартные функции: table.insert(<table>, <value>) всегда добавляется
в конец массива. table[#table + 1] = value для этого короткая. table.remove(<table>,
<index>) вернет все следующие значения, чтобы заполнить пробел (что также может
замедлить работу).
2. Проверяйте значения nil перед вставкой, избегая таких вещей, как
table.pack(function_call()) , которые могут подкрасить значения nil в нашу таблицу.
3. Проверяйте значения nil после вставки и, при необходимости, заполняя зазор,
сдвигая все последовательные значения.
4. Если возможно, используйте значения заполнитель. Например, измените значение
nil на 0 или какое-либо другое значение-заполнитель.
5. Если оставить пробелы неизбежны, это должно быть задокументировано
(прокомментировано).
6. Напишите __len() и используйте оператор # .

Пример для 6:

tab = {"john", "sansa", "daenerys", [10] = "the imp"}


print(#tab) --> prints 3
setmetatable(tab, {__len = function() return 10 end})
-- __len needs to be a function, otherwise it could just be 10
print(#tab) --> prints 10
for i=1, #tab do print(i, tab[i]) end
--> prints:
-- 1 john
-- 2 sansa
-- 3 daenerys
-- 4 nil
-- ...
-- 10 the imp

https://riptutorial.com/ru/home 78
for key, value in ipairs(tab) do print(key, value) end
--> this only prints '1 john \n 2 sansa \n 3 daenerys'

Другой альтернативой является использование функции pairs() и фильтрация нецелых


индексов:

for key in pairs(tab) do


if type(key) == "number" then
print(key, tab[key]
end
end
-- note: this does not remove float indices
-- does not iterate in order

Прочитайте таблицы онлайн: https://riptutorial.com/ru/lua/topic/676/таблицы

https://riptutorial.com/ru/home 79
глава 16: функции
Синтаксис
• funcname = function (paramA, paramB, ...) body; return exprlist end - простая функция
• function funcname (paramA, paramB, ...) body; return exprlist end - сокращенное
обозначение выше
• local funcname = function (paramA, paramB, ...) body; return exprlist end - лямбда
• local funcname ; funcname = function (paramA, paramB, ...) body; return exprlist end -
lambda, который может выполнять рекурсивные вызовы
• локальная функция funcname (paramA, paramB, ...) body; return exprlist end -
сокращенное обозначение выше
• funcname (paramA, paramB, ...) - вызов функции
• local var = var или «Default» - параметр по умолчанию
• return nil, «сообщения об ошибках» - стандартный способ прерывания с ошибкой

замечания
Функции обычно устанавливаются с function a(b,c) ... end и редко с установкой
переменной на анонимную функцию ( a = function(a,b) ... end ). Противоположное верно
при передаче функций в качестве параметров, в основном используются анонимные
функции, а обычные функции не используются так часто.

Examples

Определение функции

function add(a, b)
return a + b
end
-- creates a function called add, which returns the sum of it's two arguments

Давайте посмотрим на синтаксис. Во-первых, мы видим ключевое слово function . Ну, это
довольно описательно. Затем мы видим идентификатор add ; имя. Затем мы видим
аргументы (a, b) они могут быть любыми, и они являются локальными. Только внутри тела
функции мы можем получить к ним доступ. Давайте перейдем к концу, мы увидим ... ну, end
! И все, что находится между ними, - это тело функции; код, который запускается при его
вызове. Ключевое слово return - это то, что заставляет функцию давать полезный
результат. Без него функция ничего не возвращает, что эквивалентно возврату nil.
Разумеется, это может быть полезно для вещей, которые взаимодействуют с IO, например:

function printHello(name)

https://riptutorial.com/ru/home 80
print("Hello, " .. name .. "!");
end

В этой функции мы не использовали оператор return.

Функции также могут возвращать значения условно, что означает, что функция имеет
выбор возврата ничего (nil) или значения. Это показано в следующем примере.

function add(a, b)
if (a + b <= 100) then
return a + b -- Returns a value
else
print("This function doesn't return values over 100!") -- Returns nil
end
end

Также возможно, чтобы функция возвращала несколько значений, разделенных запятыми,


как показано:

function doOperations(a, b)
return a+b, a-b, a*b
end

added, subbed, multiplied = doOperations(4,2)

Функции также могут быть объявлены локальными

do
local function add(a, b) return a+b end
print(add(1,2)) --> prints 3
end
print(add(2, 2)) --> exits with error, because 'add' is not defined here

Они также могут быть сохранены в таблицах:

tab = {function(a,b) return a+b end}


(tab[1])(1, 2) --> returns 3

Вызов функции.

Функции полезны, если мы можем их назвать. Для вызова функции используется


следующий синтаксис:

print("Hello, World!")

Мы вызываем функцию print . Используя аргумент "Hello, World" . Как очевидно, это
приведет к выводу Hello, World в выходной поток. Возвращаемое значение доступно, как и
любая другая переменная.

https://riptutorial.com/ru/home 81
local added = add(10, 50) -- 60

Переменные также принимаются в параметрах функции.

local a = 10
local b = 60

local c = add(a, b)

print(c)

Функции, ожидающие, что таблица или строка могут быть вызваны с помощью чистого
синтаксического сахара: круглые скобки, окружающие вызов, могут быть опущены.

print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end

Анонимные функции

Создание анонимных функций


Анонимные функции похожи на обычные функции Lua, за исключением того, что у них нет
имени.

doThrice(function()
print("Hello!")
end)

Как вы можете видеть, функция не назначается ни одному имени, как print или add . Чтобы
создать анонимную функцию, все, что вам нужно сделать, это опустить имя. Эти функции
также могут принимать аргументы.

Понимание синтаксического сахара


Важно понимать, что следующий код

function double(x)
return x * 2
end

на самом деле является просто сокращением для

double = function(x)
return x * 2
end

Однако вышеуказанная функция не является анонимной, поскольку функция напрямую

https://riptutorial.com/ru/home 82
привязана к переменной!

Функции - это значения первого класса


Это означает, что функция является значением с теми же правами, что и обычные
значения, такие как числа и строки. Функции могут храниться в переменных, в таблицах,
могут передаваться как аргументы и могут быть возвращены другими функциями.

Чтобы продемонстрировать это, мы также создадим «половину» функции:

half = function(x)
return x / 2
end

Итак, теперь у нас есть две переменные, half и double , обе из которых содержат функцию
как значение. Что делать, если мы хотим создать функцию, которая будет подавать число
4 на две заданные функции и вычислять сумму обоих результатов?

Мы будем называть эту функцию, например sumOfTwoFunctions(double, half, 4) . Это будет


half функцию double функцию half и целое число 4 в нашей собственной функции.

function sumOfTwoFunctions(firstFunction, secondFunction, input)


return firstFunction(input) + secondFunction(input)
end

Вышеуказанная функция sumOfTwoFunctions показывает, как функции могут передаваться


внутри аргументов и доступны другим именем.

Параметры по умолчанию

function sayHello(name)
print("Hello, " .. name .. "!")
end

Эта функция является простой функцией, и она работает хорошо. Но что произойдет, если
мы просто sayHello() ?

stdin:2: attempt to concatenate local 'name' (a nil value)


stack traceback:
stdin:2: in function 'sayHello'
stdin:1: in main chunk
[C]: in ?

Это не совсем здорово. Это можно сделать двумя способами:

1. Вы сразу возвращаетесь из функции:

https://riptutorial.com/ru/home 83
function sayHello(name)
if not (type(name) == "string") then
return nil, "argument #1: expected string, got " .. type(name)
end -- Bail out if there's no name.
-- in lua it is a convention to return nil followed by an error message on error

print("Hello, " .. name .. "!") -- Normal behavior if name exists.


end

2. Вы устанавливаете параметр по умолчанию .

Для этого просто используйте это простое выражение

function sayHello(name)
name = name or "Jack" -- Jack is the default,
-- but if the parameter name is given,
-- name will be used instead
print("Hello, " .. name .. "!")
end

Идиома name = name or "Jack" работает, потому что or в коротких замыканиях Lua. Если
элемент с левой стороны or есть что-то другое, кроме nil или false , тогда правая сторона
никогда не будет оценена. С другой стороны, если sayHello вызывается без параметра,
тогда name будет равно nil , и поэтому строка "Jack" будет назначена для name . (Обратите
внимание, что эта идиома, следовательно, не будет работать, если логическое false
является разумным значением для рассматриваемого параметра.)

Несколько результатов

Функции в Lua могут возвращать несколько результатов.

Например:

function triple(x)
return x, x, x
end

При вызове функции, чтобы сохранить эти значения, вы должны использовать следующий
синтаксис:

local a, b, c = triple(5)

Это приведет a = b = c = 5 в этом случае a = b = c = 5 . Также можно игнорировать


возвращаемые значения, используя переменную throwaway _ в нужном месте в списке
переменных:

local a, _, c = triple(5)

В этом случае второе возвращаемое значение будет проигнорировано. Также можно

https://riptutorial.com/ru/home 84
игнорировать возвращаемые значения, не присваивая их любой переменной:

local a = triple(5)

Переменной a будет присвоено первое возвращаемое значение, а остальные два будут


отброшены.

Когда переменная сумма результатов возвращается функцией, их можно хранить в


таблице, выполняя внутри нее функцию:

local results = {triple(5)}

Таким образом, можно выполнить итерацию по таблице results чтобы увидеть, как
вернулась функция.

Заметка

В некоторых случаях это может быть неожиданностью, например:

local t = {}
table.insert(t, string.gsub(" hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert'
(number expected, got string)

Это происходит потому, что string.gsub возвращает 2 значения: заданную строку, с заменой
шаблонов и общее число совпадений.

Чтобы решить эту проблему, используйте либо промежуточную переменную, либо put ()
вокруг вызова, например:

table.insert(t, (string.gsub(" hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}

Это захватывает только первый результат вызова и игнорирует остальные.

Переменная количество аргументов

Вариадические аргументы

Именованные аргументы

local function A(name, age, hobby)


print(name .. "is " .. age .. " years old and likes " .. hobby)
end
A("john", "eating", 23) --> prints 'john is eating years old and likes 23'
-- oops, seems we got the order of the arguments wrong...
-- this happens a lot, specially with long functions that take a lot of arguments
-- and where the order doesn't follow any particular logic

local function B(tab)

https://riptutorial.com/ru/home 85
print(tab.name .. "is " .. tab.age .. " years old and likes " .. tab.hobby)
end
local john = {name="john", hobby="golf", age="over 9000", comment="plays too much golf"}
B(john)
--> will print 'John is over 9000 years old and likes golf'
-- I also added a 'comment' argument just to show that excess arguments are ignored by the
function

B({name = "tim"}) -- can also be written as


B{name = "tim"} -- to avoid cluttering the code
--> both will print 'tim is nil years old and likes nil'
-- remember to check for missing arguments and deal with them

function C(tab)
if not tab.age then return nil, "age not defined" end
tab.hobby = tab.hobby or "nothing"
-- print stuff
end

-- note that if we later decide to do a 'person' class


-- we just need to make sure that this class has the three fields
-- age, hobby and name, and it will be compatible with these functions

-- example:
local john = ClassPerson.new("John", 20, "golf") -- some sort of constructor
john.address = "some place" -- modify the object
john:do_something("information") -- call some function of the object
C(john) -- this works because objects are *usually* implemented as tables

Проверка типов аргументов

Некоторые функции работают только с определенным типом аргумента:

function foo(tab)
return tab.bar
end
--> returns nil if tab has no field bar, which is acceptable
--> returns 'attempt to index a number value' if tab is, for example, 3
--> which is unacceptable

function kungfoo(tab)
if type(tab) ~= "table" then
return nil, "take your useless " .. type(tab) .." somewhere else!"
end

return tab.bar
end

это имеет несколько последствий:

print(kungfoo(20)) --> prints 'nil, take your useless number somewhere else!'

if kungfoo(20) then print "good" else print "bad" end --> prints bad

foo = kungfoo(20) or "bar" --> sets foo to "bar"

теперь мы можем вызвать функцию с любым параметром, который мы хотим, и это не

https://riptutorial.com/ru/home 86
приведет к сбою программы.

-- if we actually WANT to abort execution on error, we can still do


result = assert(kungfoo({bar=20})) --> this will return 20
result = assert(kungfoo(20)) --> this will throw an error

Итак, что, если у нас есть функция, которая что-то делает с экземпляром определенного
класса? Это сложно, потому что классы и объекты обычно являются таблицами, поэтому
функция type вернет 'table' .

local Class = {data="important"}


local meta = {__index=Class}

function Class.new()
return setmetatable({}, meta)
end
-- this is just a very basic implementation of an object class in lua

object = Class.new()
fake = {}

print(type(object)), print(type(fake)) --> prints 'table' twice

Решение: сравните метатеги

-- continuation of previous code snippet


Class.is_instance(tab)
return getmetatable(tab) == meta
end

Class.is_instance(object) --> returns true


Class.is_instance(fake) --> returns false
Class.is_instance(Class) --> returns false
Class.is_instance("a string") --> returns false, doesn't crash the program
Class.is_instance(nil) --> also returns false, doesn't crash either

Затворы

do
local tab = {1, 2, 3}
function closure()
for key, value in ipairs(tab) do
print(key, "I can still see you")
end
end
closure()
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
end

print(tab) --> nil


-- tab is out of scope

closure()

https://riptutorial.com/ru/home 87
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
-- the function can still see tab

типичный пример использования

function new_adder(number)
return function(input)
return input + number
end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5

более продвинутый пример использования

function base64.newDecoder(str) -- Decoder factory


if #str ~= 64 then return nil, "string must be 64 characters long!" end

local tab = {}
local counter = 0
for c in str:gmatch"." do
tab[string.byte(c)] = counter
counter = counter + 1
end

return function(str)
local result = ""

for abcd in str:gmatch"..?.?.?" do


local a, b, c, d = string.byte(abcd,1,-1)
a, b, c, d = tab[a], tab[b] or 0, tab[c] or 0, tab[d] or 0
result = result .. (
string.char( ((a<<2)+(b>>4))%256 ) ..
string.char( ((b<<4)+(c>>2))%256 ) ..
string.char( ((c<<6)+d)%256 )
)
end
return result
end
end

Прочитайте функции онлайн: https://riptutorial.com/ru/lua/topic/1250/функции

https://riptutorial.com/ru/home 88
кредиты
S.
Главы Contributors
No

1971chevycamaro, Allan Burleson, Community, DarkWiiPlayer,


Начало работы с Darryl L Johnson, elektron, greatwolf, Guilherme Salazar,
1
Lua hjpotter92, hugomg, Kamiccolo, lhf, Nikola Geneshki, SoniEx2,
Telemachus

2 Pico-8 Jon Ericson

DarkWiiPlayer, engineercoding, greatwolf, Kamiccolo, Katenkyo


3 Булевы в Lua
, Samuel McKay, Telemachus

Вариадические
4 greatwolf, Kamiccolo, ktb, RamenChef, SoniEx2
аргументы

Введение в API Lua greatwolf, Jeremy Thien, Kamiccolo, Luiz Menezes, RBerteig,
5
C tversteeg

6 итераторы Adam, Egor Skriptunoff, greatwolf

7 метатаблицы DarkWiiPlayer, greatwolf, Kamiccolo, pschulz, Telemachus

8 наборы Egor Skriptunoff, Jon Ericson, ryanpattison

9 Обработка ошибок Black, DarkWiiPlayer, engineercoding, greatwolf

10 Объект-ориентация DarkWiiPlayer, Kamiccolo

Сборщик мусора и
11 greatwolf, Kamiccolo, val
слабые столы

Согласование DarkWiiPlayer, engineercoding, Eshkation, greatwolf, Kamiccolo


12
образцов , Stephen Leppik

Создание и
13 использование SoniEx2, Telemachus
модулей

14 Сопрограммы 010110110101, Bjornir, Eshkation, Kamiccolo, ktb, SoniEx2

DarkWiiPlayer, greatwolf, Hastumer, Kamiccolo, ktb, mjanicek,


15 таблицы
SoniEx2, Telemachus, Tom Blodget

16 функции Art C, Basilio German, DarkWiiPlayer, Firas Moalla, greatwolf,

https://riptutorial.com/ru/home 89
Guilherme Salazar, Jon Ericson, Katenkyo, ktb, MBorsch,
Necktrox, qaisjp, RBerteig, Romário, SoniEx2, Telemachus,
Unheilig, WolfgangTS

https://riptutorial.com/ru/home 90

Вам также может понравиться