Академический Документы
Профессиональный Документы
Культура Документы
Stack Overflow - Учусь Lua
Stack Overflow - Учусь 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
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
замечания
Версии
Дата
Версия Заметки
выхода
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
https://riptutorial.com/ru/home 2
Дата
Версия Заметки
выхода
19
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
2006-06-
5.0.3
26
2012-02-
5.1.5
17
2011-12-
5,2 Экстренный сборщик мусора, goto, финализаторы для столов.
16
2015-03-
5.2.4
07
2017-01-
5.3.4 Последняя версия.
12
https://riptutorial.com/ru/home 3
Examples
Монтаж
Бинарные
(В настоящее время Homebrew имеет 5.2.4, для 5.3 см. Homebrew / версии .)
Источник
$ 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
Примечание: вы должны указать, какую цель сборки вы хотите. В этом примере мы указали
linux . Другие доступные цели сборки включают solaris , aix , bsd , freebsd , macosx , mingw и т.
Д. Для получения более подробной информации macosx doc/readme.html , который включен в
источник. (Вы также можете найти последнюю версию README в Интернете .)
Модули
https://riptutorial.com/ru/home 4
Стандартные библиотеки ограничены примитивами:
Все эти библиотеки могут быть отключены для конкретной сборки или загружены во время
выполнения.
Комментарии
--[[
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'
--]]
---[[
print'Lua is lovely'
--]]
Сделав еще один шаг, два блока кода могут быть настроены таким образом, что если
первый блок будет закомментирован, второй не будет, и наоборот:
---[[
print 'Lua is love'
--[=[]]
print 'Lua is life'
--]=]
--[[
print 'Lua is love'
--[=[]]
print 'Lua is life'
--]=]
function bottle_take(bottles_available)
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
return bottles_left
end
local bottle_count = 99
while true do
bottle_count = bottle_take(bottle_count)
end
Сама программа может быть запущена, выполнив следующую команду в своей оболочке:
$ lua bottles_of_mate.lua
https://riptutorial.com/ru/home 7
$ luac -l bottles_of_mate.lua
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
Начиная
переменные
типы
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'
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.
https://riptutorial.com/ru/home 9
foo = nil -- Here we set 'foo' to nil so that it can be garbage-collected.
выражения
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.
Определение функций
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 а пустая строка
оценивается как истина.
вывоз мусора
таблицы
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.
условия
if (condition) then
-- do something
elseif (other_condition) then
-- do something else
else
-- do something
end
для петель
Существует два типа цикла for в Lua: числовое значение for цикла и общий for цикла.
Третье выражение в числовой for цикла представляет собой этап , по которому цикл
будет увеличиваться. Это упрощает выполнение обратных циклов:
for a=10, 1, -1 do
print(a) --> 10, 9, 8, 7, 6, etc.
end
for a=1, 10 do
print(a) --> 1, 2, 3, 4, 5, etc.
end
Также обратите внимание, что переменная цикла является локальной for цикла for .
Он не будет существовать после завершения цикла.
https://riptutorial.com/ru/home 11
• Общие for циклов работают через все значения, возвращаемые функцией итератора:
делать блоки
local a = 10
do
print(a) --> 10
local a = 20
print(a) --> 20
end
print(a) --> 10
Иногда Lua не ведет себя так, как можно было бы подумать после прочтения
документации. Некоторые из этих случаев:
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) .
Привет, мир
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
Игровой цикл
• _update()
function _draw()
cls()
print("a winner is you")
end
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
if (btn(4)) _init()
function _update60()
x = stat(32)
y = stat(33)
-- 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
https://riptutorial.com/ru/home 17
глава 3: Булевы в Lua
замечания
Булевы, истина и ложность прямолинейны в Lua. Для обзора:
Examples
Булевский тип
В lua есть только два значения, которые вычисляют false: nil и false , тогда как все
остальное, включая числовое значение 0 оценивается как true.
Логические операции
Логические операторы в lua необязательно возвращают логические значения:
and вернет второе значение, если первое значение будет равно true;
https://riptutorial.com/ru/home 18
Это позволяет моделировать тернарный оператор, как и на других языках:
Это также можно использовать для инициализации таблиц, если они не существуют
или избегать использования операторов 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)
Единственный случай, когда это не применяется, - это когда переменная хранит значение
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 может появляться в состоянии. Правила для оценки просты:
https://riptutorial.com/ru/home 19
2. Все остальное считается истинным.
if 1 then
print("Numbers work.")
end
if 0 then
print("Even 0 is true")
end
Логические операторы
Приказ о приоритете
Порядок приоритета подобен математическим операторам унарный - , * и + :
• not
• затем and
• затем or
Краткосрочная оценка
Операторы 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
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
https://riptutorial.com/ru/home 21
print(true and false or 1) -- outputs 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
Для тех, кто в ней нуждается, вот две функции, представляющие эти логические
операторы.
https://riptutorial.com/ru/home 22
Синтаксис
условие и правша_expr или falsey_expr
Использование в переменной
присваивании / инициализации
local drink = (fruit == "apple") and "apple juice" or "water"
Предостережение
Бывают ситуации, когда этот механизм не имеет желаемого поведения. Рассмотрим этот
случай
https://riptutorial.com/ru/home 23
В «реальном» тернарном операторе ожидаемое значение var является false . В Lua,
однако, and оценка «проваливается» , потому что второй операнд falsey. В результате var
заканчивается, а should not happen вместо этого.
Два возможных решения этой проблемы реорганизуют это выражение, поэтому средний
операнд не является ложным. например.
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).
Vararg полезен тем, что длина vararg соответствует любым явно переданным (или
вычисленным) нулям. Например.
https://riptutorial.com/ru/home 25
function test(...)
return select('#', ...)
end
test() --> 0
test(nil, 1, nil) --> 3
Идиоматическое использование
Поскольку varargs несут свою длину, люди используют их как последовательности, чтобы
избежать проблем с отверстиями в таблицах. Это не было их предполагаемое
использование, и одна эталонная реализация Lua не оптимизирована. Хотя такое
использование рассматривается в примерах, оно, как правило, неодобрительно.
Examples
основы
function id(...)
return
end
Если вы назвали эту функцию id(1, 2, 3, 4, 5) тогда ... (AKA - список vararg) будет
содержать значения 1, 2, 3, 4, 5 .
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
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
Расширенное использование
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
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
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/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.
Кроме того, есть некоторые дополнительные функции, такие как прямой доступ к
внутреннему реестру, изменение поведения стандартного распределителя памяти или
сборщика мусора.
Examples
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
https://riptutorial.com/ru/home 30
int main(void)
{
5,1
5,1
/* do stuff with Lua VM. In this case just load and execute a file: */
luaL_dofile(L, "some_input_file.lua");
return EXIT_SUCCESS;
}
#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);
lua_close(lvm_hnd);
return EXIT_SUCCESS;
https://riptutorial.com/ru/home 31
}
НОТА:
В этом примере мы хотим, чтобы настроение было настроено с помощью сценария Lua. Вот
mood.lua:
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);
/* load script */
luaL_dofile(l, "mood.lua");
lua_close(l);
return 0;
}
И вот вывод:
Обработка таблиц
Чтобы получить доступ или изменить индекс в таблице, вам нужно как-то поместить
таблицу в стек.
https://riptutorial.com/ru/home 33
Предположим, что для этих примеров ваша таблица является глобальной переменной с
именем tbl.
Как мы видели, все, что вам нужно сделать, - это нажать таблицу в стек, нажать индекс и
вызвать lua_gettable. аргумент -2 означает, что таблица является вторым элементом из
вершины стека.
lua_gettable запускает метаметоды. Если вы не хотите запускать метаметоды, вместо этого
используйте lua_rawget. Он использует те же аргументы.
return 0;
}
То же упражнение, что и получение контента. Вам нужно нажать на стек, нажать индекс,
а затем нажать значение в стек. после этого вы вызываете lua_settable. аргумент -3 - это
позиция таблицы в стеке. Чтобы избежать запуска метаметодов, используйте lua_rawset
вместо lua_settable. Он использует те же аргументы.
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 , на четвертый элемент
сверху.
Для удобства уборки я очистил верхний элемент, так что только две таблицы остаются в
стеке.
https://riptutorial.com/ru/home 35
глава 6: итераторы
Examples
Итераторы используют форму цикла for известного как общий для цикла .
Мы можем написать цикл for для итерации всех пар ключ-значение в таблице с
использованием следующей функции.
Стандартные итераторы
Для итератора по всем ключам и значениям в любой таблице мы можем использовать пар
функции библиотеки.
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 функцию.
Итератор Ipairs
Мы можем реализовать ipairs без ipairs в двух отдельных функциях.
Итератор символов
Мы можем создать новые итераторы без сохранения состояния, выполнив контракт
генератора 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
Истребители состояния
Использование таблиц
Состояние добавления может быть упаковано в общий для инвариантного состояния цикла
.
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
Использование закрытий
Дополнительное состояние можно обернуть в пределах закрытия функции. Поскольку
состояние полностью содержится в объеме замыкания, инвариантное состояние и
управляющая переменная не нужны.
Использование Coroutines
Дополнительное состояние может содержаться внутри сопрограммы, снова не требуется
инвариантное состояние и управляющая переменная.
https://riptutorial.com/ru/home 39
print(c) --> a, b, c, d, e
end
https://riptutorial.com/ru/home 40
глава 7: метатаблицы
Синтаксис
• [[local] mt =] getmetatable ( t ) -> получить связанный метатебель для ' t '
• [[local] t =] setmetatable ( t , mt ) -> установить метатебель для ' t ' в ' mt ' и возвращает '
t'
параметры
параметр подробности
замечания
Есть некоторые метаметоды, не упомянутые здесь. Полный список и их использование см.
В соответствующей записи в руководстве lua .
Examples
-- 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 }'
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
5,2
Объекты в lua собирают мусор. Иногда вам нужно освободить некоторый ресурс, хотите
распечатать сообщение или сделать что-то еще, когда объект будет уничтожен (собран).
Для этого вы можете использовать __gc , который __gc с объектом в качестве аргумента
при уничтожении объекта. Вы могли видеть этот метаметод как своего рода деструктор.
local meta =
{
__gc = function(self)
print("destroying self: " .. self.name)
end
}
https://riptutorial.com/ru/home 42
local t = { name = "inner" }
setmetatable(t, meta)
end
Больше метаметодов
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
}
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
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 meta = {}
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 = {}
Это можно использовать для повышения ошибки при чтении несуществующего ключа:
Пишу
local meta = {}
-- create a testobject
local t = { }
Теперь вы можете спросить себя, как фактическое значение написано в таблице. В этом
случае это не так. Проблема здесь в том, что метаметоды могут вызывать метаметоды, что
приведет к инфинитивному циклу или, точнее, переполнению стека. Итак, как мы можем
это решить? Решение для этого называется доступным доступом к таблице .
Иногда вы не хотите запускать метаметоды, но на самом деле пишете или читаете именно
данный ключ, без каких-либо умных функций, обернутых вокруг доступа. Для этого 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
}
Моделирование ООП
Методы экземпляра могут быть записаны путем передачи объекта в качестве первого
аргумента.
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"
https://riptutorial.com/ru/home 47
глава 8: наборы
Examples
Там нет встроенного способа поиска списка для определенного элемента. Однако
программирование в Lua показывает, как вы можете создать набор, который может
помочь:
if items["orange"] then
-- do something
end
Создать набор
set.peach = true
set.apple = true
-- alternatively
https://riptutorial.com/ru/home 48
set['banana'] = true
set['strawberry'] = true
set.apple = nil
Тест членства
if set.strawberry then
print "We've got strawberries"
end
https://riptutorial.com/ru/home 49
глава 9: Обработка ошибок
Examples
Использование pcall
Синтаксис:
pcall( f , arg1,···)
Возвращаемые значения:
1. status (boolean)
pcall может использоваться для различных случаев, однако общий из них заключается в
том, чтобы ловить ошибки из функции, предоставленной вашей функции. Например,
скажем, у нас есть эта функция:
Когда заданные ошибки функции при запуске 3, сообщение об ошибке будет понятно
пользователю, что оно не исходит от вашей функции, а от функции, которая была
https://riptutorial.com/ru/home 50
предоставлена нашей функции. Кроме того, учитывая это, фантастический BSoD может
быть уведомлен о пользователе. Однако это зависит от приложения, которое реализует
эту функцию, поскольку API, скорее всего, этого не сделает.
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
function square(a)
return a * "a"
end
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
print ("Status: ", status) -- will print "true" because no errors were thrown
print ("Return Value: ", retval) -- will print "100"
print ("Hello World") -- Prints "Hello World"
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
Теперь у нас есть функция, которая ведет себя так, мы можем делать такие вещи:
И если мы хотим, чтобы программа потерпела крах, если что-то пошло не так, мы все равно
можем это сделать:
К счастью, нам даже не нужно писать все это каждый раз; lua имеет функцию, которая
выполняет именно это
result = assert(foo(20))
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()
или же
И использовать его:
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)
Имейте в виду, что таблицы «значения» являются только ссылкой; на самом деле
существует только одна фактическая таблица для всех экземпляров объекта, если
конструктор не определен как 1 , поэтому, делая это, мы модифицируем поведение всех
экземпляров класса.
setmetatable(
object,
setmetatable(
{__call=table.concat},
{__index=getmetatable(object)}
)
)
Как это работает? - Мы создаем новый метатебель, как в ошибке №1, но вместо того,
чтобы оставить его пустым, мы создаем мягкую копию оригинального метатега. Можно
https://riptutorial.com/ru/home 54
сказать, что новый метатебель «наследует» от исходного, как если бы он был экземпляром
класса. Теперь мы можем переопределять значения исходного метатета без их
модификации.
Изменение класса:
1-й (рекомендуется):
https://riptutorial.com/ru/home 55
глава 11: Сборщик мусора и слабые столы
Синтаксис
1. collectgarbage (gcrule [, gcdata]) - собирать мусор, используя gcrule
2. setmetatable (tab, {__mode = weakmode}) - установить слабый режим вкладки в слабом
режиме
параметры
параметр подробности
Тип слабой таблицы: "k" (только слабые клавиши), "v" (только слабые
weakmode
значения), "vk" (слабые клавиши и значения)
Examples
Слабые столы
t1, t2, t3, t4 = nil, nil, nil, nil -- No more "strong" references to t3 and t4
print(#maintab, #weaktab) --> 2 4
https://riptutorial.com/ru/home 56
глава 12: Согласование образцов
Синтаксис
• string.find (str, pattern [, init [, plain]]) - возвращает начальный и конечный индекс
соответствия в str
• %d все цифры
https://riptutorial.com/ru/home 57
замечания
В некоторых примерах используется нотация (<string literal>):function <string literal> ,
которая эквивалентна string.function(<string literal>, <string literal>) потому что все
строки имеют метатебель с __index полем __index к таблице string .
Examples
Соответствие Lua
%d цифры (0-9)
%s символы пробела
%U заглавные буквы
%г символ с представлением 0
Как упоминалось выше, любая версия этого класса в верхнем регистре представляет
дополнение к классу. Например, %D будет соответствовать любой несимметричной
символьной последовательности:
https://riptutorial.com/ru/home 58
string.match("f123", "%D") --> f
( ) % . + - * [ ? ^ $
символ Модификатор
https://riptutorial.com/ru/home 59
шаблонами.
Персонаж Описание
string.find (Введение)
Функция find
Представление шаблонов
("hello world"):find ".- " -- will match characters until it finds a space
--> so it will return 1, 6
https://riptutorial.com/ru/home 60
Все, кроме следующих символов, представляют собой ^$()%.[]*+-?) . Любой из этих
символов может быть представлен символом % после самого символа.
("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`
https://riptutorial.com/ru/home 61
--> #af
--> #dd
--> #e6
end
Представляем захваты:
Это очень похоже на регулярную функцию, однако она вернет только захваты вместо
полного соответствия.
Функция gsub
аргумент функции
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
аргумент таблицы
sub = {}
sub["hello"] = "g'day"
sub["world"] = "m8"
https://riptutorial.com/ru/home 63
глава 13: Создание и использование
модулей
замечания
Основной шаблон для написания модуля - заполнить таблицу ключами, которые являются
именами функций и значениями, которые являются самими функциями. Затем модуль
возвращает эту функцию для вызова кода, который require и использует. (Функции
являются первоклассными значениями в Lua, поэтому хранение функции в таблице
является простым и обычным.) Таблица также может содержать любые важные константы
в форме, скажем, строк или чисел.
Examples
Написание модуля
-- 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
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"
https://riptutorial.com/ru/home 65
глава 14: Сопрограммы
Синтаксис
• coroutine.create (function) возвращает сопрограмму (type (coroutine) == 'thread'),
содержащую эту функцию.
замечания
Система coroutine была реализована в lua для эмуляции многопоточности, существующей
на других языках. Он работает, переключаясь с чрезвычайно высокой скоростью между
различными функциями, чтобы пользователь-пользователь считал, что они выполняются
одновременно.
Examples
https://riptutorial.com/ru/home 66
аргументом: функция с исполняемым кодом:
thread1 = coroutine.create(function()
print("honk")
end)
print(thread1)
-->> thread: 6b028b8c
print(coroutine.status(thread1))
-->> suspended
coroutine.resume(thread1)
-->> honk
print(coroutine.status(thread1))
-->> dead
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
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
coroutine.resume(thread3)
-->> honk again stackoverflow
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
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
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 и для создания
модульной системы.
Установка значения в таблице на 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.
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 нет определенного порядка для
обхода, даже если ключи таблицы являются числовыми .
Будьте ipairs() что итерация с помощью ipairs() не будет работать, как вы могли бы
захотеть в нескольких случаях:
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
Основное использование
Доступ к элементам
Назначение элементов
https://riptutorial.com/ru/home 73
example_table[2] = "Constipation"
print(example_table[2]) --> Constipation
Удаление элементов
Как указано ранее, значение по умолчанию для ключа без присвоенного значения равно
nil . Удаление элемента из таблицы так же просто, как сброс значения ключа до значения
по умолчанию.
Длина стола
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
https://riptutorial.com/ru/home 74
замечаний.
Эти две функции мутируют данную таблицу. Как вы могли бы сказать второй метод вызова
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
https://riptutorial.com/ru/home 75
Под массивом здесь мы понимаем таблицу Lua, используемую в качестве
последовательности. Например:
Важным моментом в нашем массиве pets является отсутствие пробелов. Первым пунктом,
pets[1] , является строка «собаки», второй пункт, pets[2] , это строка «кошки», а последний
предмет, pets[3] , - «птицы». Стандартная библиотека Lua и большинство модулей,
написанных для Lua, принимают 1 как первый индекс для последовательностей. Таким
образом, безщеточный массив имеет элементы из 1..n не пропуская никаких чисел в
последовательности. (В предельном случае n = 1 , и массив имеет только один элемент в
нем).
Это напечатает «Позиция в позиции 1 - это собаки». «Позиция в позиции 2 - это кошки».
«Пункт в позиции 3 - птицы».
{"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 .
• При использовании других функций, которые (прямо или косвенно) доступны для
любого из вышеперечисленных.
Чтобы избежать этой проблемы, важно написать свой код, чтобы, если вы ожидаете, что
таблица будет массивом, вы не вводите пробелы. Пробелы можно вводить несколькими
способами:
Вы можете подумать: «Но я никогда не сделаю ничего из этого». Ну, не намеренно, но вот
конкретный пример того, как все может пойти не так. Представьте, что вы хотите написать
метод фильтрации для Lua, например, Ruby's select и Perl's grep . Метод примет тестовую
функцию и массив. Он выполняет итерацию по массиву, в свою очередь вызывает метод
тестирования по каждому элементу. Если элемент проходит, то этот элемент добавляется
в массив результатов, который возвращается в конце метода. Ниже приведена
некорректная реализация:
return res
end
https://riptutorial.com/ru/home 77
Проблема в том, что когда функция возвращает false , мы пропускаем число в
последовательности. Представьте, что вызывающий filter(isodd, {1,2,3,4,5,6,7,8,9,10}) : в
возвращаемой таблице будут пробелы каждый раз, когда в массиве, переданном filter ,
есть четное число.
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:
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'
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
Функции также могут возвращать значения условно, что означает, что функция имеет
выбор возврата ничего (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
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
Вызов функции.
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
Анонимные функции
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 на две заданные функции и вычислять сумму обоих результатов?
Параметры по умолчанию
function sayHello(name)
print("Hello, " .. name .. "!")
end
Эта функция является простой функцией, и она работает хорошо. Но что произойдет, если
мы просто sayHello() ?
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
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
является разумным значением для рассматриваемого параметра.)
Несколько результатов
Например:
function triple(x)
return x, x, x
end
При вызове функции, чтобы сохранить эти значения, вы должны использовать следующий
синтаксис:
local a, b, c = triple(5)
local a, _, c = triple(5)
https://riptutorial.com/ru/home 84
игнорировать возвращаемые значения, не присваивая их любой переменной:
local a = 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 ()
вокруг вызова, например:
Вариадические аргументы
Именованные аргументы
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
function C(tab)
if not tab.age then return nil, "age not defined" end
tab.hobby = tab.hobby or "nothing"
-- print stuff
end
-- 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
https://riptutorial.com/ru/home 86
приведет к сбою программы.
Итак, что, если у нас есть функция, которая что-то делает с экземпляром определенного
класса? Это сложно, потому что классы и объекты обычно являются таблицами, поэтому
функция type вернет 'table' .
function Class.new()
return setmetatable({}, meta)
end
-- this is just a very basic implementation of an object class in lua
object = Class.new()
fake = {}
Затворы
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
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
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 = ""
https://riptutorial.com/ru/home 88
кредиты
S.
Главы Contributors
No
Вариадические
4 greatwolf, Kamiccolo, ktb, RamenChef, SoniEx2
аргументы
Введение в API Lua greatwolf, Jeremy Thien, Kamiccolo, Luiz Menezes, RBerteig,
5
C tversteeg
Сборщик мусора и
11 greatwolf, Kamiccolo, val
слабые столы
Создание и
13 использование SoniEx2, Telemachus
модулей
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